Merge branch 'master' into tmp-doc
diff --git a/.jenkins b/.jenkins
new file mode 100755
index 0000000..b933b31
--- /dev/null
+++ b/.jenkins
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+set -e
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+for i in `find "$DIR/.jenkins.d" -type f -perm +111 | sort`; do
+ echo "Run: $i"
+ $i
+done
\ No newline at end of file
diff --git a/.jenkins.d/10-ndn-cxx.sh b/.jenkins.d/10-ndn-cxx.sh
new file mode 100755
index 0000000..951e3f9
--- /dev/null
+++ b/.jenkins.d/10-ndn-cxx.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+set -x
+set -e
+
+cd /tmp
+BUILD="no"
+if [ ! -d ndn-cxx ]; then
+ git clone --depth 1 git://github.com/named-data/ndn-cxx
+ cd ndn-cxx
+ BUILD="yes"
+else
+ cd ndn-cxx
+ INSTALLED_VERSION=`git rev-parse HEAD || echo NONE`
+ sudo rm -Rf latest-version
+ git clone --depth 1 git://github.com/named-data/ndn-cxx latest-version
+ cd latest-version
+ LATEST_VERSION=`git rev-parse HEAD || echo UNKNOWN`
+ cd ..
+ rm -Rf latest-version
+ if [ "$INSTALLED_VERSION" != "$LATEST_VERSION" ]; then
+ cd ..
+ sudo rm -Rf ndn-cxx
+ git clone --depth 1 git://github.com/named-data/ndn-cxx
+ cd ndn-cxx
+ BUILD="yes"
+ fi
+fi
+
+sudo rm -Rf /usr/local/include/ndn-cxx
+sudo rm -f /usr/local/lib/libndn-cxx*
+sudo rm -f /usr/local/lib/pkgconfig/libndn-cxx*
+
+if [ "$BUILD" = "yes" ]; then
+ sudo ./waf distclean -j1 --color=yes
+fi
+
+./waf configure -j1 --color=yes --without-osx-keychain
+./waf -j1 --color=yes
+sudo ./waf install -j1 --color=yes
diff --git a/.jenkins.d/20-build.sh b/.jenkins.d/20-build.sh
new file mode 100755
index 0000000..8220c31
--- /dev/null
+++ b/.jenkins.d/20-build.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+set -x
+set -e
+
+sudo rm -Rf /usr/local/include/ChronoSync
+sudo rm -f /usr/local/lib/libChronoSync*
+sudo rm -f /usr/local/lib/pkgconfig/ChronoSync*
+
+# Cleanup
+sudo ./waf -j1 --color=yes distclean
+
+# Configure/build in debug mode
+./waf -j1 --color=yes configure --with-tests --debug
+./waf -j1 --color=yes build
+
+# Cleanup
+sudo ./waf -j1 --color=yes distclean
+
+# Configure/build in optimized mode without tests
+./waf -j1 --color=yes configure
+./waf -j1 --color=yes build
+
+# Cleanup
+sudo ./waf -j1 --color=yes distclean
+
+# Configure/build in optimized mode with tests
+./waf -j1 --color=yes configure --with-tests
+./waf -j1 --color=yes build
+
+# Install
+sudo ./waf install -j1 --color=yes
+sudo ldconfig || true
diff --git a/.jenkins.d/30-test.sh b/.jenkins.d/30-test.sh
new file mode 100755
index 0000000..4109bd5
--- /dev/null
+++ b/.jenkins.d/30-test.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+set -x
+set -e
+
+./build/unit-tests -l test_suite
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..63e80e5
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,15 @@
+# For Ubuntu platform only
+language: cpp
+os:
+ - linux
+compiler:
+ - gcc
+notifications:
+ email:
+ on_success: always
+ on_failure: always
+before_install:
+ - travis_retry sudo apt-get update
+ - travis_retry sudo apt-get install -qq libssl-dev libprotobuf-dev protobuf-compiler libboost1.48-all-dev libcrypto++-dev libsqlite3-dev
+script:
+ - ./.jenkins
diff --git a/.waf-tools/default-compiler-flags.py b/.waf-tools/default-compiler-flags.py
index 600f260..225833a 100644
--- a/.waf-tools/default-compiler-flags.py
+++ b/.waf-tools/default-compiler-flags.py
@@ -9,20 +9,13 @@
def options(opt):
opt.add_option('--debug', '--with-debug', action='store_true', default=False, dest='debug',
help='''Compile in debugging mode without all optimizations (-O0)''')
- opt.add_option('--with-c++11', action='store_true', default=False, dest='use_cxx11',
- help='''Enable C++11 mode (experimental, may not work)''')
def configure(conf):
areCustomCxxflagsPresent = (len(conf.env.CXXFLAGS) > 0)
- defaultFlags = []
-
- if conf.options.use_cxx11:
- defaultFlags += ['-std=c++0x', '-std=c++11']
- else:
- defaultFlags += ['-std=c++03', '-Wno-variadic-macros', '-Wno-c99-extensions']
-
- defaultFlags += ['-Wall', '-Wno-long-long', '-Wno-unneeded-internal-declaration',
- '-Wno-c++11-extensions', '-Wno-nested-anon-types']
+ defaultFlags = ['-std=c++0x', '-std=c++11',
+ '-stdlib=libc++', # clang on OSX < 10.9 by default uses gcc's
+ # libstdc++, which is not C++11 compatible
+ '-Wall', '-Wno-long-long', '-Wno-nested-anon-types']
if conf.options.debug:
conf.define('_DEBUG', 1)
@@ -32,6 +25,7 @@
'-fcolor-diagnostics', # clang
'-fdiagnostics-color', # gcc >= 4.9
'-Wno-error=maybe-uninitialized', # Bug #1560
+ '-Wno-unneeded-internal-declaration',
]
if areCustomCxxflagsPresent:
missingFlags = [x for x in defaultFlags if x not in conf.env.CXXFLAGS]
@@ -46,12 +40,15 @@
if not areCustomCxxflagsPresent:
conf.add_supported_cxxflags(defaultFlags)
+ # clang on OSX < 10.9 by default uses gcc's libstdc++, which is not C++11 compatible
+ conf.add_supported_linkflags(['-stdlib=libc++'])
+
@Configure.conf
def add_supported_cxxflags(self, cxxflags):
"""
Check which cxxflags are supported by compiler and add them to env.CXXFLAGS variable
"""
- self.start_msg('Checking allowed flags for c++ compiler')
+ self.start_msg('Checking supported CXXFLAGS')
supportedFlags = []
for flag in cxxflags:
@@ -60,3 +57,18 @@
self.end_msg(' '.join(supportedFlags))
self.env.CXXFLAGS = supportedFlags + self.env.CXXFLAGS
+
+@Configure.conf
+def add_supported_linkflags(self, linkflags):
+ """
+ Check which linkflags are supported by compiler and add them to env.LINKFLAGS variable
+ """
+ self.start_msg('Checking supported LINKFLAGS')
+
+ supportedFlags = []
+ for flag in linkflags:
+ if self.check_cxx(linkflags=['-Werror', flag], mandatory=False):
+ supportedFlags += [flag]
+
+ self.end_msg(' '.join(supportedFlags))
+ self.env.LINKFLAGS = supportedFlags + self.env.LINKFLAGS
diff --git a/common.hpp b/common.hpp
index 50555fc..1e0d02f 100644
--- a/common.hpp
+++ b/common.hpp
@@ -26,15 +26,15 @@
#include "config.hpp"
#ifdef _TESTS
-#define VIRTUAL_WITH_TESTS virtual
-#define PUBLIC_WITH_TESTS_ELSE_PROTECTED public
-#define PUBLIC_WITH_TESTS_ELSE_PRIVATE public
-#define PROTECTED_WITH_TESTS_ELSE_PRIVATE protected
+#define CHRONOSYNC_VIRTUAL_WITH_TESTS virtual
+#define CHRONOSYNC_PUBLIC_WITH_TESTS_ELSE_PROTECTED public
+#define CHRONOSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE public
+#define CHRONOSYNC_PROTECTED_WITH_TESTS_ELSE_PRIVATE protected
#else
-#define VIRTUAL_WITH_TESTS
-#define PUBLIC_WITH_TESTS_ELSE_PROTECTED protected
-#define PUBLIC_WITH_TESTS_ELSE_PRIVATE private
-#define PROTECTED_WITH_TESTS_ELSE_PRIVATE private
+#define CHRONOSYNC_VIRTUAL_WITH_TESTS
+#define CHRONOSYNC_PUBLIC_WITH_TESTS_ELSE_PROTECTED protected
+#define CHRONOSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE private
+#define CHRONOSYNC_PROTECTED_WITH_TESTS_ELSE_PRIVATE private
#endif
#include <cstddef>
@@ -50,10 +50,12 @@
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/assert.hpp>
+#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/noncopyable.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/scoped_ptr.hpp>
+#include <boost/tuple/tuple.hpp>
namespace chronosync {
@@ -81,7 +83,7 @@
using ndn::Block;
namespace tlv {
-using namespace ndn::Tlv;
+using namespace ndn::tlv;
}
namespace name = ndn::name;
diff --git a/src/sync-intro-certificate.h b/obsolete/sync-intro-certificate.h
similarity index 100%
rename from src/sync-intro-certificate.h
rename to obsolete/sync-intro-certificate.h
diff --git a/src/sync-validator.cc b/obsolete/sync-validator.cc
similarity index 100%
rename from src/sync-validator.cc
rename to obsolete/sync-validator.cc
diff --git a/src/sync-validator.h b/obsolete/sync-validator.h
similarity index 100%
rename from src/sync-validator.h
rename to obsolete/sync-validator.h
diff --git a/tests/unit-tests/test-sync-validator.cpp.outdated b/obsolete/tests/test-sync-validator.cpp.outdated
similarity index 100%
rename from tests/unit-tests/test-sync-validator.cpp.outdated
rename to obsolete/tests/test-sync-validator.cpp.outdated
diff --git a/src/sync-name-info.cc b/src/diff-state-container.cpp
similarity index 86%
copy from src/sync-name-info.cc
copy to src/diff-state-container.cpp
index eb7411a..38a67f3 100644
--- a/src/sync-name-info.cc
+++ b/src/diff-state-container.cpp
@@ -19,15 +19,7 @@
* @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
* @author Chaoyi Bian <bcy@pku.edu.cn>
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
*/
-#include "sync-name-info.h"
-
-// #include <boost/lexical_cast.hpp>
-
-namespace Sync {
-
-NameInfo::NameMap NameInfo::m_names;
-size_t NameInfo::m_ids = 0;
-
-} // Sync
+#include "diff-state-container.hpp"
diff --git a/src/sync-diff-state-container.h b/src/diff-state-container.hpp
similarity index 70%
rename from src/sync-diff-state-container.h
rename to src/diff-state-container.hpp
index 428877e..d20fbee 100644
--- a/src/sync-diff-state-container.h
+++ b/src/diff-state-container.hpp
@@ -19,35 +19,47 @@
* @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
* @author Chaoyi Bian <bcy@pku.edu.cn>
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
*/
-#ifndef SYNC_DIFF_STATE_CONTAINER_H
-#define SYNC_DIFF_STATE_CONTAINER_H
+#ifndef CHRONOSYNC_DIFF_STATE_CONTAINER_HPP
+#define CHRONOSYNC_DIFF_STATE_CONTAINER_HPP
-#include "sync-diff-state.h"
-#include "sync-digest.h"
+#include "mi-tag.hpp"
+#include "diff-state.hpp"
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/tag.hpp>
-// #include <boost/multi_index/ordered_index.hpp>
-// #include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
-// #include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>
+namespace chronosync {
+
namespace mi = boost::multi_index;
-namespace Sync {
+struct DigestPtrHash
+{
+ std::size_t
+ operator()(ndn::ConstBufferPtr digest) const
+ {
+ BOOST_ASSERT(digest->size() > sizeof(std::size_t));
-/// @cond include_hidden
-struct sequenced { };
-struct timed { };
-/// @endcond
+ return *reinterpret_cast<const std::size_t*>(digest->buf());
+ }
+};
+
+struct DigestPtrEqual
+{
+ bool
+ operator()(ndn::ConstBufferPtr digest1, ndn::ConstBufferPtr digest2) const
+ {
+ return *digest1 == *digest2;
+ }
+};
/**
- * \ingroup sync
* @brief Container for differential states
*/
struct DiffStateContainer : public mi::multi_index_container<
@@ -56,11 +68,11 @@
// For fast access to elements using DiffState hashes
mi::hashed_unique<
mi::tag<hashed>,
- mi::const_mem_fun<DiffState, DigestConstPtr, &DiffState::getDigest>,
+ mi::const_mem_fun<DiffState, ndn::ConstBufferPtr, &DiffState::getRootDigest>,
DigestPtrHash,
DigestPtrEqual
- >
- ,
+ >,
+
// sequenced index to access older/newer element (like in list)
mi::sequenced<mi::tag<sequenced> >
>
@@ -68,6 +80,6 @@
{
};
-} // Sync
+} // namespace chronosync
-#endif // SYNC_DIFF_STATE_CONTAINER_H
+#endif // CHRONOSYNC_DIFF_STATE_CONTAINER_HPP
diff --git a/src/sync-seq-no.cc b/src/diff-state.cpp
similarity index 76%
rename from src/sync-seq-no.cc
rename to src/diff-state.cpp
index aba1672..f5813bf 100644
--- a/src/sync-seq-no.cc
+++ b/src/diff-state.cpp
@@ -19,23 +19,25 @@
* @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
* @author Chaoyi Bian <bcy@pku.edu.cn>
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/web/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
*/
-#include "sync-seq-no.h"
-#include <boost/make_shared.hpp>
+#include "diff-state.hpp"
-using namespace boost;
+namespace chronosync {
-namespace Sync {
-
-DigestConstPtr
-SeqNo::getDigest () const
+ConstStatePtr
+DiffState::diff() const
{
- DigestPtr digest = make_shared<Digest> ();
- *digest << m_session << m_seq;
- digest->finalize ();
- return digest;
+ StatePtr result = make_shared<State>();
+
+ ConstDiffStatePtr state = m_next;
+ while (static_cast<bool>(state)) {
+ *result += *state;
+ state = state->m_next;
+ }
+
+ return result;
}
-} // Sync
+} // namespace chronosync
diff --git a/src/diff-state.hpp b/src/diff-state.hpp
new file mode 100644
index 0000000..46ed412
--- /dev/null
+++ b/src/diff-state.hpp
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOSYNC_DIFF_STATE_HPP
+#define CHRONOSYNC_DIFF_STATE_HPP
+
+#include "state.hpp"
+
+namespace chronosync {
+
+class DiffState;
+typedef shared_ptr<DiffState> DiffStatePtr;
+typedef shared_ptr<const DiffState> ConstDiffStatePtr;
+
+/**
+ * @brief Contains the diff info between two states.
+ *
+ * DiffState is used to construct DiffLog. It serves as
+ * a log entry. Each log entry contains the updates between
+ * two states, and is indexed by the digest of the second state
+ * which is the result when the updates have been applied.
+ *
+ * DiffLog is a chain of DiffStates. Each DiffState connects to
+ * the next DiffState (a newer diff) through member m_next. The
+ * m_next of the last DiffState in a log should be empty. And the
+ * root digest of the last DiffState in the log should be the most
+ * current state.
+ */
+class DiffState : public State
+{
+public:
+ /**
+ * @brief Set successor for the diff state
+ *
+ * @param next successor state
+ */
+ void
+ setNext(ConstDiffStatePtr next)
+ {
+ m_next = next;
+ }
+
+ /**
+ * @brief Set digest for the diff state (obtained from a corresponding full state)
+ *
+ * @param digest root digest of the full state
+ */
+ void
+ setRootDigest(ndn::ConstBufferPtr digest)
+ {
+ m_digest = digest;
+ }
+
+ /**
+ * @brief Get root digest of the full state after applying the diff state
+ */
+ ndn::ConstBufferPtr
+ getRootDigest() const
+ {
+ return m_digest;
+ }
+
+ /**
+ * @brief Accumulate differences from this state to the most current state
+ *
+ * This method assumes that the DiffState is in a log. It will iterate the all
+ * the DiffState between its m_next DiffState and the last DiffState in the log,
+ * and aggregate all the differences into one diff, which is represented as a
+ * State object.
+ *
+ * @returns Accumulated differences from this state to the most current state
+ */
+ ConstStatePtr
+ diff() const;
+
+private:
+ ConstDiffStatePtr m_next;
+ ndn::ConstBufferPtr m_digest;
+};
+
+} // chronosync
+
+#endif // CHRONOSYNC_DIFF_STATE_HPP
diff --git a/src/sync-seq-no.cc b/src/interest-container.cpp
similarity index 73%
copy from src/sync-seq-no.cc
copy to src/interest-container.cpp
index aba1672..4d42321 100644
--- a/src/sync-seq-no.cc
+++ b/src/interest-container.cpp
@@ -19,23 +19,20 @@
* @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
* @author Chaoyi Bian <bcy@pku.edu.cn>
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/web/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
*/
-#include "sync-seq-no.h"
-#include <boost/make_shared.hpp>
+#include "interest-container.hpp"
-using namespace boost;
+namespace chronosync {
-namespace Sync {
-
-DigestConstPtr
-SeqNo::getDigest () const
+UnsatisfiedInterest::UnsatisfiedInterest(shared_ptr<const Interest> interest,
+ ndn::ConstBufferPtr digest,
+ bool isUnknown)
+ : interest(interest)
+ , digest(digest)
+ , isUnknown(isUnknown)
{
- DigestPtr digest = make_shared<Digest> ();
- *digest << m_session << m_seq;
- digest->finalize ();
- return digest;
}
-} // Sync
+} // namespace chronosync
diff --git a/src/interest-container.hpp b/src/interest-container.hpp
new file mode 100644
index 0000000..bf971eb
--- /dev/null
+++ b/src/interest-container.hpp
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOSYNC_INTEREST_CONTAINER_HPP
+#define CHRONOSYNC_INTEREST_CONTAINER_HPP
+
+#include "common.hpp"
+#include "mi-tag.hpp"
+#include "diff-state-container.hpp"
+
+#include <ndn-cxx/util/time.hpp>
+#include <ndn-cxx/util/scheduler.hpp>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/tag.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/member.hpp>
+
+namespace chronosync {
+
+namespace mi = boost::multi_index;
+
+/// @brief Entry to record unsatisfied Sync Interest
+class UnsatisfiedInterest : noncopyable
+{
+public:
+ UnsatisfiedInterest(shared_ptr<const Interest> interest,
+ ndn::ConstBufferPtr digest,
+ bool isUnknown = false);
+
+public:
+ shared_ptr<const Interest> interest;
+ ndn::ConstBufferPtr digest;
+ bool isUnknown;
+ ndn::EventId expirationEvent;
+};
+
+typedef shared_ptr<UnsatisfiedInterest> UnsatisfiedInterestPtr;
+typedef shared_ptr<const UnsatisfiedInterest> ConstUnsatisfiedInterestPtr;
+
+/**
+ * @brief Container for unsatisfied Sync Interests
+ */
+struct InterestContainer : public mi::multi_index_container<
+ UnsatisfiedInterestPtr,
+ mi::indexed_by<
+ mi::hashed_unique<
+ mi::tag<hashed>,
+ mi::member<UnsatisfiedInterest, ndn::ConstBufferPtr, &UnsatisfiedInterest::digest>,
+ DigestPtrHash,
+ DigestPtrEqual
+ >
+ >
+ >
+{
+};
+
+} // namespace chronosync
+
+#endif // CHRONOSYNC_INTEREST_CONTAINER_HPP
diff --git a/src/interest-table.cpp b/src/interest-table.cpp
new file mode 100644
index 0000000..5b97f0a
--- /dev/null
+++ b/src/interest-table.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#include "interest-table.hpp"
+
+namespace chronosync {
+
+InterestTable::InterestTable(boost::asio::io_service& io)
+ : m_scheduler(io)
+{
+}
+
+InterestTable::~InterestTable()
+{
+ clear();
+}
+
+bool
+InterestTable::insert(shared_ptr<const Interest> interest,
+ ndn::ConstBufferPtr digest,
+ bool isKnown/*=false*/)
+{
+ bool doesExist = erase(digest);
+
+ UnsatisfiedInterestPtr request =
+ make_shared<UnsatisfiedInterest>(interest, digest, isKnown);
+
+ time::milliseconds entryLifetime = interest->getInterestLifetime();
+ if (entryLifetime < time::milliseconds::zero())
+ entryLifetime = ndn::DEFAULT_INTEREST_LIFETIME;
+
+ request->expirationEvent =
+ m_scheduler.scheduleEvent(entryLifetime,
+ [=] () { quiteErase(digest); });
+
+ m_table.insert(request);
+
+ return doesExist;
+}
+
+void
+InterestTable::quiteErase(ndn::ConstBufferPtr digest)
+{
+ erase(digest);
+}
+
+bool
+InterestTable::erase(ndn::ConstBufferPtr digest)
+{
+ InterestContainer::index<hashed>::type::iterator it = m_table.get<hashed>().find(digest);
+ if (it != m_table.get<hashed>().end()) {
+ m_scheduler.cancelEvent((*it)->expirationEvent);
+ m_table.erase(it);
+ return true;
+ }
+ return false;
+}
+
+size_t
+InterestTable::size() const
+{
+ return m_table.size();
+}
+
+void
+InterestTable::clear()
+{
+ for (InterestContainer::iterator it = m_table.begin();
+ it != m_table.end(); it++) {
+ m_scheduler.cancelEvent((*it)->expirationEvent);
+ }
+
+ m_table.clear();
+}
+
+}
diff --git a/src/interest-table.hpp b/src/interest-table.hpp
new file mode 100644
index 0000000..d85661c
--- /dev/null
+++ b/src/interest-table.hpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOSYNC_INTEREST_TABLE_HPP
+#define CHRONOSYNC_INTEREST_TABLE_HPP
+
+#include "interest-container.hpp"
+
+#include <ndn-cxx/util/scheduler.hpp>
+
+namespace chronosync {
+
+/**
+ * @brief A table to keep unsatisfied Sync Interest
+ */
+class InterestTable : noncopyable
+{
+public:
+ class Error : public std::runtime_error
+ {
+ public:
+ explicit
+ Error(const std::string& what)
+ : std::runtime_error(what)
+ {
+ }
+ };
+
+ typedef InterestContainer::iterator iterator;
+ typedef InterestContainer::const_iterator const_iterator;
+
+ explicit
+ InterestTable(boost::asio::io_service& io);
+
+ ~InterestTable();
+
+ /**
+ * @brief Insert an interest
+ *
+ * If the interest already exists in the table, the old interest will be replaced,
+ * and expire timer will be reset.
+ * Interests with the same name are counted as the same.
+ * This method assumes that the sync prefix of all interests are the same
+ * thus it only compares the digest part.
+ *
+ * @param interest Interest to insert.
+ * @param digest The value of the last digest component.
+ * @param isKnown false if the digest is an unknown digest.
+ * @return true if the same interest exists in the table before insertion.
+ */
+ bool
+ insert(shared_ptr<const Interest> interest,
+ ndn::ConstBufferPtr digest,
+ bool isKnown = false);
+
+ /**
+ * @brief Delete interest by digest (e.g., when it was satisfied)
+ *
+ * @return true if an interest with the digest exists in the table before deletion
+ */
+ bool
+ erase(ndn::ConstBufferPtr digest);
+
+ void
+ quiteErase(ndn::ConstBufferPtr digest);
+
+ const_iterator
+ begin() const
+ {
+ return m_table.begin();
+ }
+
+ iterator
+ begin()
+ {
+ return m_table.begin();
+ }
+
+ const_iterator
+ end() const
+ {
+ return m_table.end();
+ }
+
+ iterator
+ end()
+ {
+ return m_table.end();
+ }
+
+ size_t
+ size() const;
+
+ void
+ clear();
+
+private:
+ ndn::Scheduler m_scheduler;
+ ndn::time::steady_clock::Duration m_entryLifetime;
+ ndn::time::steady_clock::Duration m_cleanPeriod;
+
+ InterestContainer m_table;
+};
+
+} // namespace chronosync
+
+#endif // CHRONOSYNC_INTEREST_TABLE_HPP
diff --git a/src/sync-name-info.cc b/src/leaf-container.cpp
similarity index 86%
rename from src/sync-name-info.cc
rename to src/leaf-container.cpp
index eb7411a..8bad5eb 100644
--- a/src/sync-name-info.cc
+++ b/src/leaf-container.cpp
@@ -19,15 +19,8 @@
* @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
* @author Chaoyi Bian <bcy@pku.edu.cn>
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
*/
-#include "sync-name-info.h"
-
-// #include <boost/lexical_cast.hpp>
-
-namespace Sync {
-
-NameInfo::NameMap NameInfo::m_names;
-size_t NameInfo::m_ids = 0;
-
-} // Sync
+#include "leaf-container.hpp"
+// Simply check whether LeafContainer can compile
diff --git a/src/leaf-container.hpp b/src/leaf-container.hpp
new file mode 100644
index 0000000..51470b7
--- /dev/null
+++ b/src/leaf-container.hpp
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOSYNC_LEAF_CONTAINER_HPP
+#define CHRONOSYNC_LEAF_CONTAINER_HPP
+
+#include "mi-tag.hpp"
+#include "leaf.hpp"
+
+#include <ndn-cxx/util/crypto.hpp>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/mem_fun.hpp>
+
+
+
+namespace chronosync {
+
+namespace mi = boost::multi_index;
+
+struct SessionNameHash
+{
+ std::size_t
+ operator()(const Name& prefix) const
+ {
+ ndn::ConstBufferPtr buffer =
+ ndn::crypto::sha256(prefix.wireEncode().wire(), prefix.wireEncode().size());
+
+ BOOST_ASSERT(buffer->size() > sizeof(std::size_t));
+
+ return *reinterpret_cast<const std::size_t*>(buffer->buf());
+ }
+};
+
+struct SessionNameEqual
+{
+ bool
+ operator()(const Name& prefix1, const Name& prefix2) const
+ {
+ return prefix1 == prefix2;
+ }
+};
+
+struct SessionNameCompare : public std::less<Name>
+{
+ bool
+ operator()(const Name& prefix1, const Name& prefix2) const
+ {
+ return prefix1 < prefix2;
+ }
+};
+
+/**
+ * @brief Container for chronosync leaves
+ */
+struct LeafContainer : public mi::multi_index_container<
+ LeafPtr,
+ mi::indexed_by<
+ // For fast access to elements using SessionName
+ mi::hashed_unique<
+ mi::tag<hashed>,
+ mi::const_mem_fun<Leaf, const Name&, &Leaf::getSessionName>,
+ SessionNameHash,
+ SessionNameEqual
+ >,
+
+ mi::ordered_unique<
+ mi::tag<ordered>,
+ mi::const_mem_fun<Leaf, const Name&, &Leaf::getSessionName>,
+ SessionNameCompare
+ >
+ >
+ >
+{
+};
+
+} // namespace chronosync
+
+#endif // CHRONOSYNC_LEAF_CONTAINER_HPP
diff --git a/src/leaf.cpp b/src/leaf.cpp
new file mode 100644
index 0000000..129b1ea
--- /dev/null
+++ b/src/leaf.cpp
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#include "leaf.hpp"
+
+namespace chronosync {
+
+Leaf::Leaf(const Name& sessionName, const SeqNo& seq)
+ : m_sessionName(sessionName)
+ , m_seq(seq)
+{
+ updateDigest();
+}
+
+Leaf::Leaf(const Name& userPrefix, uint64_t session, const SeqNo& seq)
+ : m_sessionName(userPrefix)
+ , m_seq(seq)
+{
+ m_sessionName.appendNumber(session);
+ updateDigest();
+}
+
+Leaf::~Leaf()
+{
+}
+
+ndn::ConstBufferPtr
+Leaf::getDigest() const
+{
+ return m_digest.computeDigest();
+}
+
+void
+Leaf::setSeq(const SeqNo& seq)
+{
+ if (seq > m_seq) {
+ m_seq = seq;
+ updateDigest();
+ }
+}
+
+void
+Leaf::updateDigest()
+{
+ m_digest.reset();
+ m_digest << getSessionName().wireEncode() << getSeq();
+ m_digest.computeDigest();
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Leaf& leaf)
+{
+ os << leaf.getSessionName() << "(" << leaf.getSeq() << ")";
+ return os;
+}
+
+
+} // namespace chronosync
diff --git a/src/leaf.hpp b/src/leaf.hpp
new file mode 100644
index 0000000..b26b975
--- /dev/null
+++ b/src/leaf.hpp
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOSYNC_LEAF_HPP
+#define CHRONOSYNC_LEAF_HPP
+
+#include "common.hpp"
+#include <ndn-cxx/util/digest.hpp>
+
+namespace chronosync {
+
+typedef uint64_t SeqNo;
+
+/**
+ * @brief Sync tree leaf
+ *
+ * The leaf node should be copyable when used to construct diff between two states.
+ */
+class Leaf
+{
+public:
+ Leaf(const Name& sessionName, const SeqNo& seq);
+
+ Leaf(const Name& userPrefix, uint64_t session, const SeqNo& seq);
+
+ virtual
+ ~Leaf();
+
+ const Name&
+ getSessionName() const
+ {
+ return m_sessionName;
+ }
+
+ const SeqNo&
+ getSeq() const
+ {
+ return m_seq;
+ }
+
+ ndn::ConstBufferPtr
+ getDigest() const;
+
+ /**
+ * @brief Update sequence number of the leaf
+ * @param seq Sequence number
+ *
+ * If seq is no greater than getSeq(), this operation has no effect.
+ */
+ virtual void
+ setSeq(const SeqNo& seq);
+
+private:
+ void
+ updateDigest();
+
+private:
+ Name m_sessionName;
+ SeqNo m_seq;
+
+ mutable ndn::util::Sha256 m_digest;
+};
+
+typedef shared_ptr<Leaf> LeafPtr;
+typedef shared_ptr<const Leaf> ConstLeafPtr;
+
+std::ostream&
+operator<<(std::ostream& os, const Leaf& leaf);
+
+} // namespace chronosync
+
+#endif // CHRONOSYNC_LEAF_HPP
diff --git a/src/sync-logging.h b/src/logger.hpp
similarity index 75%
rename from src/sync-logging.h
rename to src/logger.hpp
index 3d2f5d3..2ac782a 100644
--- a/src/sync-logging.h
+++ b/src/logger.hpp
@@ -21,35 +21,32 @@
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
*/
-#ifndef SYNC_LOG_H
-#define SYNC_LOG_H
+#ifndef CHRONOSYNC_LOGGER_HPP
+#define CHRONOSYNC_LOGGER_HPP
#ifdef HAVE_LOG4CXX
#include <log4cxx/logger.h>
#define INIT_LOGGER(name) \
- static log4cxx::LoggerPtr staticModuleLogger = log4cxx::Logger::getLogger (name);
+ static log4cxx::LoggerPtr staticModuleLogger = log4cxx::Logger::getLogger(name)
#define _LOG_DEBUG(x) \
- LOG4CXX_DEBUG(staticModuleLogger, x);
+ LOG4CXX_DEBUG(staticModuleLogger, x)
#define _LOG_TRACE(x) \
- LOG4CXX_TRACE(staticModuleLogger, x);
+ LOG4CXX_TRACE(staticModuleLogger, x)
#define _LOG_FUNCTION(x) \
- LOG4CXX_TRACE(staticModuleLogger, __FUNCTION__ << "(" << x << ")");
+ LOG4CXX_TRACE(staticModuleLogger, __FUNCTION__ << "(" << x << ")")
#define _LOG_FUNCTION_NOARGS \
- LOG4CXX_TRACE(staticModuleLogger, __FUNCTION__ << "()");
+ LOG4CXX_TRACE(staticModuleLogger, __FUNCTION__ << "()")
#define _LOG_ERROR(x) \
- LOG4CXX_ERROR(staticModuleLogger, x);
+ LOG4CXX_ERROR(staticModuleLogger, x)
-void
-INIT_LOGGERS ();
-
-#else
+#else // HAVE_LOG4CXX
#define INIT_LOGGER(name)
#define _LOG_FUNCTION(x)
@@ -60,18 +57,20 @@
#ifdef _DEBUG
-#include <boost/date_time/posix_time/posix_time.hpp>
-#include <boost/thread/thread_time.hpp>
-#include <boost/thread/thread.hpp>
+#include <thread>
#include <iostream>
+#include <ndn-cxx/util/time.hpp>
#define _LOG_DEBUG(x) \
- std::clog << boost::get_system_time () << " " << boost::this_thread::get_id () << " " << x << std::endl;
+ std::clog << ndn::time::system_clock::now() << " " << std::this_thread::get_id() << \
+ " " << x << std::endl
-#else
+#else // _DEBUG
+
#define _LOG_DEBUG(x)
-#endif
+
+#endif // _DEBUG
#endif // HAVE_LOG4CXX
-#endif // SYNC_LOG_H
+#endif // CHRONOSYNC_LOGGER_HPP
diff --git a/src/logic.cpp b/src/logic.cpp
new file mode 100644
index 0000000..f584da5
--- /dev/null
+++ b/src/logic.cpp
@@ -0,0 +1,593 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#include "logic.hpp"
+#include "logger.hpp"
+
+INIT_LOGGER("Logic");
+
+#ifdef _DEBUG
+#define _LOG_DEBUG_ID(v) _LOG_DEBUG("Instance" << m_instanceId << ": " << v)
+#else
+#define _LOG_DEBUG_ID(v) _LOG_DEBUG(v)
+#endif
+
+namespace chronosync {
+
+using ndn::ConstBufferPtr;
+using ndn::EventId;
+
+const uint8_t EMPTY_DIGEST_VALUE[] = {
+ 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
+ 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
+ 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
+ 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55
+};
+
+#ifdef _DEBUG
+int Logic::m_instanceCounter = 0;
+#endif
+
+const time::steady_clock::Duration Logic::DEFAULT_RESET_TIMER = time::seconds(0);
+const time::steady_clock::Duration Logic::DEFAULT_CANCEL_RESET_TIMER = time::milliseconds(500);
+const time::milliseconds Logic::DEFAULT_RESET_INTEREST_LIFETIME(1000);
+const time::milliseconds Logic::DEFAULT_SYNC_INTEREST_LIFETIME(1000);
+const time::milliseconds Logic::DEFAULT_SYNC_REPLY_FRESHNESS(1000);
+
+const ndn::ConstBufferPtr Logic::EMPTY_DIGEST(new ndn::Buffer(EMPTY_DIGEST_VALUE, 32));
+const ndn::name::Component Logic::RESET_COMPONENT("reset");
+
+Logic::Logic(ndn::Face& face,
+ const Name& syncPrefix,
+ const Name& userPrefix,
+ const UpdateCallback& onUpdate,
+ const time::steady_clock::Duration& resetTimer,
+ const time::steady_clock::Duration& cancelResetTimer,
+ const time::milliseconds& resetInterestLifetime,
+ const time::milliseconds& syncInterestLifetime,
+ const time::milliseconds& syncReplyFreshness)
+ : m_face(face)
+ , m_syncPrefix(syncPrefix)
+ , m_userPrefix(userPrefix)
+ , m_interestTable(m_face.getIoService())
+ , m_outstandingInterestId(0)
+ , m_isInReset(false)
+ , m_needPeriodReset(resetTimer > time::steady_clock::Duration::zero())
+ , m_onUpdate(onUpdate)
+ , m_scheduler(m_face.getIoService())
+ , m_randomGenerator(static_cast<unsigned int>(std::time(0)))
+ , m_rangeUniformRandom(m_randomGenerator, boost::uniform_int<>(100,500))
+ , m_reexpressionJitter(m_randomGenerator, boost::uniform_int<>(100,500))
+ , m_resetTimer(resetTimer)
+ , m_cancelResetTimer(cancelResetTimer)
+ , m_resetInterestLifetime(resetInterestLifetime)
+ , m_syncInterestLifetime(syncInterestLifetime)
+ , m_syncReplyFreshness(syncReplyFreshness)
+{
+#ifdef _DEBUG
+ m_instanceId = m_instanceCounter++;
+#endif
+
+ _LOG_DEBUG_ID(">> Logic::Logic");
+
+ m_syncReset = m_syncPrefix;
+ m_syncReset.append("reset");
+
+ _LOG_DEBUG_ID("Listen to: " << m_syncPrefix);
+ m_syncRegisteredPrefixId =
+ m_face.setInterestFilter(m_syncPrefix,
+ bind(&Logic::onSyncInterest, this, _1, _2),
+ bind(&Logic::onSyncRegisterFailed, this, _1, _2));
+
+ setUserPrefix(m_userPrefix);
+
+ _LOG_DEBUG_ID("<< Logic::Logic");
+}
+
+Logic::~Logic()
+{
+ m_face.unsetInterestFilter(m_syncRegisteredPrefixId);
+ m_scheduler.cancelAllEvents();
+}
+
+void
+Logic::reset()
+{
+ m_isInReset = true;
+
+ m_state.reset();
+ m_log.clear();
+
+ sendResetInterest();
+
+ // reset outstanding interest name, so that data for previous interest will be dropped.
+ if (m_outstandingInterestId != 0) {
+ m_face.removePendingInterest(m_outstandingInterestId);
+ m_outstandingInterestId = 0;
+ }
+
+ sendSyncInterest();
+
+ if (static_cast<bool>(m_delayedInterestProcessingId))
+ m_scheduler.cancelEvent(m_delayedInterestProcessingId);
+
+ m_delayedInterestProcessingId =
+ m_scheduler.scheduleEvent(m_cancelResetTimer,
+ bind(&Logic::cancelReset, this));
+}
+
+void
+Logic::setUserPrefix(const Name& userPrefix)
+{
+ m_userPrefix = userPrefix;
+
+ m_sessionName = m_userPrefix;
+ m_sessionName.appendNumber(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count());
+
+ m_seqNo = 0;
+
+ reset();
+}
+
+void
+Logic::updateSeqNo(const SeqNo& seqNo)
+{
+ _LOG_DEBUG_ID(">> Logic::updateSeqNo");
+ _LOG_DEBUG_ID("seqNo: " << seqNo << " m_seqNo: " << m_seqNo);
+ if (seqNo < m_seqNo || seqNo == 0)
+ return;
+
+ m_seqNo = seqNo;
+
+ _LOG_DEBUG_ID("updateSeqNo: m_seqNo " << m_seqNo);
+
+ if (!m_isInReset) {
+ _LOG_DEBUG_ID("updateSeqNo: not in Reset ");
+ ndn::ConstBufferPtr previousRoot = m_state.getRootDigest();
+ {
+ using namespace CryptoPP;
+
+ std::string hash;
+ StringSource(previousRoot->buf(), previousRoot->size(), true,
+ new HexEncoder(new StringSink(hash), false));
+ _LOG_DEBUG_ID("Hash: " << hash);
+ }
+
+ bool isInserted = false;
+ bool isUpdated = false;
+ SeqNo oldSeq;
+ boost::tie(isInserted, isUpdated, oldSeq) = m_state.update(m_sessionName, seqNo);
+
+ _LOG_DEBUG_ID("Insert: " << std::boolalpha << isInserted);
+ _LOG_DEBUG_ID("Updated: " << std::boolalpha << isUpdated);
+ if (isInserted || isUpdated) {
+ DiffStatePtr commit = make_shared<DiffState>();
+ commit->update(m_sessionName, seqNo);
+ commit->setRootDigest(m_state.getRootDigest());
+ insertToDiffLog(commit, previousRoot);
+
+ satisfyPendingSyncInterests(commit);
+ }
+ }
+}
+
+ConstBufferPtr
+Logic::getRootDigest() const
+{
+ return m_state.getRootDigest();
+}
+
+void
+Logic::printState(std::ostream& os) const
+{
+ BOOST_FOREACH(ConstLeafPtr leaf, m_state.getLeaves())
+ {
+ os << *leaf << "\n";
+ }
+}
+
+std::set<Name>
+Logic::getSessionNames() const
+{
+ std::set<Name> sessionNames;
+
+ BOOST_FOREACH(ConstLeafPtr leaf, m_state.getLeaves())
+ {
+ sessionNames.insert(leaf->getSessionName());
+ }
+
+ return sessionNames;
+}
+
+void
+Logic::onSyncInterest(const Name& prefix, const Interest& interest)
+{
+ _LOG_DEBUG_ID(">> Logic::onSyncInterest");
+ Name name = interest.getName();
+
+ _LOG_DEBUG_ID("InterestName: " << name);
+
+ if (RESET_COMPONENT != name.get(-1)) {
+ // normal sync interest
+ processSyncInterest(interest.shared_from_this());
+ }
+ else
+ // reset interest
+ processResetInterest(interest);
+
+ _LOG_DEBUG_ID("<< Logic::onSyncInterest");
+}
+
+void
+Logic::onSyncRegisterFailed(const Name& prefix, const std::string& msg)
+{
+ //Sync prefix registration failed
+ _LOG_DEBUG_ID(">> Logic::onSyncRegisterFailed");
+}
+
+void
+Logic::onSyncData(const Interest& interest, Data& data)
+{
+ _LOG_DEBUG_ID(">> Logic::onSyncData");
+ // Place holder for validator.
+ onSyncDataValidated(data.shared_from_this());
+ _LOG_DEBUG_ID("<< Logic::onSyncData");
+}
+
+void
+Logic::onResetData(const Interest& interest, Data& data)
+{
+ // This should not happened, drop the received data.
+}
+
+void
+Logic::onSyncTimeout(const Interest& interest)
+{
+ // It is OK. Others will handle the time out situation.
+ _LOG_DEBUG_ID(">> Logic::onSyncTimeout");
+ _LOG_DEBUG_ID("Interest: " << interest.getName());
+ _LOG_DEBUG_ID("<< Logic::onSyncTimeout");
+}
+
+void
+Logic::onSyncDataValidationFailed(const shared_ptr<const Data>& data)
+{
+ // SyncReply cannot be validated.
+}
+
+void
+Logic::onSyncDataValidated(const shared_ptr<const Data>& data)
+{
+ Name name = data->getName();
+ ConstBufferPtr digest = make_shared<ndn::Buffer>(name.get(-1).value(), name.get(-1).value_size());
+
+ processSyncData(name, digest, data->getContent().blockFromValue());
+}
+
+void
+Logic::processSyncInterest(const shared_ptr<const Interest>& interest,
+ bool isTimedProcessing/*=false*/)
+{
+ _LOG_DEBUG_ID(">> Logic::processSyncInterest");
+
+ const Name& name = interest->getName();
+ ConstBufferPtr digest =
+ make_shared<ndn::Buffer>(name.get(-1).value(), name.get(-1).value_size());
+
+ ConstBufferPtr rootDigest = m_state.getRootDigest();
+
+ // If the digest of the incoming interest is the same as root digest
+ // Put the interest into InterestTable
+ if (*rootDigest == *digest) {
+ _LOG_DEBUG_ID("Oh, we are in the same state");
+ m_interestTable.insert(interest, digest, false);
+
+ if (!m_isInReset)
+ return;
+
+ if (!isTimedProcessing) {
+ _LOG_DEBUG_ID("Non timed processing in reset");
+ // Still in reset, our own seq has not been put into state yet
+ // Do not hurry, some others may be also resetting and may send their reply
+ if (static_cast<bool>(m_delayedInterestProcessingId))
+ m_scheduler.cancelEvent(m_delayedInterestProcessingId);
+
+ time::milliseconds after(m_rangeUniformRandom());
+ _LOG_DEBUG_ID("After: " << after);
+ m_delayedInterestProcessingId =
+ m_scheduler.scheduleEvent(after,
+ bind(&Logic::processSyncInterest, this, interest, true));
+ }
+ else {
+ _LOG_DEBUG_ID("Timed processing in reset");
+ // Now we can get out of reset state by putting our own stuff into m_state.
+ cancelReset();
+ }
+
+ return;
+ }
+
+ // If the digest of incoming interest is an "empty" digest
+ if (digest == EMPTY_DIGEST) {
+ _LOG_DEBUG_ID("Poor guy, he knows nothing");
+ sendSyncData(name, m_state);
+ return;
+ }
+
+ DiffStateContainer::iterator stateIter = m_log.find(digest);
+ // If the digest of incoming interest can be found from the log
+ if (stateIter != m_log.end()) {
+ _LOG_DEBUG_ID("It is ok, you are so close");
+ sendSyncData(name, *(*stateIter)->diff());
+ return;
+ }
+
+ if (!isTimedProcessing) {
+ _LOG_DEBUG_ID("Let's wait, just wait for a while");
+ // Do not hurry, some incoming SyncReplies may help us to recognize the digest
+ bool doesExist = m_interestTable.insert(interest, digest, true);
+ if (doesExist)
+ // Original comment (not sure): somebody else replied, so restart random-game timer
+ // YY: Get the same SyncInterest again, refresh the timer
+ m_scheduler.cancelEvent(m_delayedInterestProcessingId);
+
+ m_delayedInterestProcessingId =
+ m_scheduler.scheduleEvent(time::milliseconds(m_rangeUniformRandom()),
+ bind(&Logic::processSyncInterest, this, interest, true));
+ }
+ else {
+ // OK, nobody is helping us, just tell the truth.
+ _LOG_DEBUG_ID("OK, nobody is helping us, just tell the truth");
+ m_interestTable.erase(digest);
+ sendSyncData(name, m_state);
+ }
+
+ _LOG_DEBUG_ID("<< Logic::processSyncInterest");
+}
+
+void
+Logic::processResetInterest(const Interest& interest)
+{
+ _LOG_DEBUG_ID(">> Logic::processResetInterest");
+ reset();
+}
+
+void
+Logic::processSyncData(const Name& name,
+ ndn::ConstBufferPtr digest,
+ const Block& syncReplyBlock)
+{
+ _LOG_DEBUG_ID(">> Logic::processSyncData");
+
+ DiffStatePtr commit = make_shared<DiffState>();
+ ndn::ConstBufferPtr previousRoot = m_state.getRootDigest();
+
+ try {
+ m_interestTable.erase(digest); // Remove satisfied interest from PIT
+
+ State reply;
+ reply.wireDecode(syncReplyBlock);
+
+ std::vector<MissingDataInfo> v;
+ BOOST_FOREACH(ConstLeafPtr leaf, reply.getLeaves().get<ordered>())
+ {
+ BOOST_ASSERT(leaf != 0);
+
+ const Name& info = leaf->getSessionName();
+ SeqNo seq = leaf->getSeq();
+
+ bool isInserted = false;
+ bool isUpdated = false;
+ SeqNo oldSeq;
+ boost::tie(isInserted, isUpdated, oldSeq) = m_state.update(info, seq);
+
+ if (isInserted || isUpdated) {
+ commit->update(info, seq);
+
+ oldSeq++;
+ MissingDataInfo mdi = {info, oldSeq, seq};
+ v.push_back(mdi);
+ }
+ }
+
+ if (!v.empty()) {
+ m_onUpdate(v);
+
+ commit->setRootDigest(m_state.getRootDigest());
+ insertToDiffLog(commit, previousRoot);
+ }
+ else {
+ _LOG_DEBUG_ID("What? nothing new");
+ }
+ }
+ catch (State::Error&) {
+ _LOG_DEBUG_ID("Something really fishy happened during state decoding");
+ // Something really fishy happened during state decoding;
+ commit.reset();
+ return;
+ }
+
+ if (static_cast<bool>(commit) && !commit->getLeaves().empty()) {
+ // state changed and it is safe to express a new interest
+ time::steady_clock::Duration after = time::milliseconds(m_reexpressionJitter());
+ _LOG_DEBUG_ID("Reschedule sync interest after: " << after);
+ EventId eventId = m_scheduler.scheduleEvent(after,
+ bind(&Logic::sendSyncInterest, this));
+
+ m_scheduler.cancelEvent(m_reexpressingInterestId);
+ m_reexpressingInterestId = eventId;
+ }
+}
+
+void
+Logic::satisfyPendingSyncInterests(ConstDiffStatePtr commit)
+{
+ _LOG_DEBUG_ID(">> Logic::satisfyPendingSyncInterests");
+ try {
+ _LOG_DEBUG_ID("InterestTable size: " << m_interestTable.size());
+ for (InterestTable::const_iterator it = m_interestTable.begin();
+ it != m_interestTable.end(); it++) {
+ ConstUnsatisfiedInterestPtr request = *it;
+
+ if (request->isUnknown)
+ sendSyncData(request->interest->getName(), m_state);
+ else
+ sendSyncData(request->interest->getName(), *commit);
+ }
+ m_interestTable.clear();
+ }
+ catch (InterestTable::Error&) {
+ // ok. not really an error
+ }
+ _LOG_DEBUG_ID("<< Logic::satisfyPendingSyncInterests");
+}
+
+void
+Logic::insertToDiffLog(DiffStatePtr commit, ndn::ConstBufferPtr previousRoot)
+{
+ _LOG_DEBUG_ID(">> Logic::insertToDiffLog");
+ // Connect to the history
+ if (!m_log.empty())
+ (*m_log.find(previousRoot))->setNext(commit);
+
+ // Insert the commit
+ m_log.erase(commit->getRootDigest());
+ m_log.insert(commit);
+ _LOG_DEBUG_ID("<< Logic::insertToDiffLog");
+}
+
+void
+Logic::sendResetInterest()
+{
+ _LOG_DEBUG_ID(">> Logic::sendResetInterest");
+
+ if (m_needPeriodReset) {
+ _LOG_DEBUG_ID("Need Period Reset");
+ _LOG_DEBUG_ID("ResetTimer: " << m_resetTimer);
+
+ EventId eventId =
+ m_scheduler.scheduleEvent(m_resetTimer + ndn::time::milliseconds(m_reexpressionJitter()),
+ bind(&Logic::sendResetInterest, this));
+ m_scheduler.cancelEvent(m_resetInterestId);
+ m_resetInterestId = eventId;
+ }
+
+ Interest interest(m_syncReset);
+ interest.setMustBeFresh(true);
+ interest.setInterestLifetime(m_resetInterestLifetime);
+ m_face.expressInterest(interest,
+ bind(&Logic::onResetData, this, _1, _2),
+ bind(&Logic::onSyncTimeout, this, _1));
+
+ _LOG_DEBUG_ID("<< Logic::sendResetInterest");
+}
+
+void
+Logic::sendSyncInterest()
+{
+ _LOG_DEBUG_ID(">> Logic::sendSyncInterest");
+
+ Name interestName;
+ interestName.append(m_syncPrefix)
+ .append(ndn::name::Component(*m_state.getRootDigest()));
+
+ m_outstandingInterestName = interestName;
+
+#ifdef _DEBUG
+ printDigest(m_state.getRootDigest());
+#endif
+
+ EventId eventId =
+ m_scheduler.scheduleEvent(m_syncInterestLifetime +
+ ndn::time::milliseconds(m_reexpressionJitter()),
+ bind(&Logic::sendSyncInterest, this));
+ m_scheduler.cancelEvent(m_reexpressingInterestId);
+ m_reexpressingInterestId = eventId;
+
+ Interest interest(interestName);
+ interest.setMustBeFresh(true);
+ interest.setInterestLifetime(m_syncInterestLifetime);
+
+ m_outstandingInterestId = m_face.expressInterest(interest,
+ bind(&Logic::onSyncData, this, _1, _2),
+ bind(&Logic::onSyncTimeout, this, _1));
+
+ _LOG_DEBUG_ID("Send interest: " << interest.getName());
+ _LOG_DEBUG_ID("<< Logic::sendSyncInterest");
+}
+
+void
+Logic::sendSyncData(const Name& name, const State& state)
+{
+ _LOG_DEBUG_ID(">> Logic::sendSyncData");
+ shared_ptr<Data> syncReply = make_shared<Data>(name);
+ syncReply->setContent(state.wireEncode());
+ syncReply->setFreshnessPeriod(m_syncReplyFreshness);
+ m_keyChain.sign(*syncReply);
+
+ m_face.put(*syncReply);
+
+ // checking if our own interest got satisfied
+ if (m_outstandingInterestName == name) {
+ // remove outstanding interest
+ if (m_outstandingInterestId != 0) {
+ m_face.removePendingInterest(m_outstandingInterestId);
+ m_outstandingInterestId = 0;
+ }
+
+ // re-schedule sending Sync interest
+ time::milliseconds after(m_reexpressionJitter());
+ _LOG_DEBUG_ID("Satisfy our own interest");
+ _LOG_DEBUG_ID("Reschedule sync interest after " << after);
+ EventId eventId = m_scheduler.scheduleEvent(after, bind(&Logic::sendSyncInterest, this));
+ m_scheduler.cancelEvent(m_reexpressingInterestId);
+ m_reexpressingInterestId = eventId;
+ }
+ _LOG_DEBUG_ID("<< Logic::sendSyncData");
+}
+
+void
+Logic::cancelReset()
+{
+ _LOG_DEBUG_ID(">> Logic::cancelReset");
+ if (!m_isInReset)
+ return;
+
+ m_isInReset = false;
+ updateSeqNo(m_seqNo);
+ _LOG_DEBUG_ID("<< Logic::cancelReset");
+}
+
+void
+Logic::printDigest(ndn::ConstBufferPtr digest)
+{
+ using namespace CryptoPP;
+
+ std::string hash;
+ StringSource(digest->buf(), digest->size(), true,
+ new HexEncoder(new StringSink(hash), false));
+ _LOG_DEBUG_ID("Hash: " << hash);
+}
+
+} // namespace chronosync
diff --git a/src/logic.hpp b/src/logic.hpp
new file mode 100644
index 0000000..cd5cd29
--- /dev/null
+++ b/src/logic.hpp
@@ -0,0 +1,388 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOSYNC_LOGIC_HPP
+#define CHRONOSYNC_LOGIC_HPP
+
+#include "boost-header.h"
+#include <memory>
+#include <map>
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/util/scheduler.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+
+#include "interest-table.hpp"
+#include "diff-state-container.hpp"
+
+namespace chronosync {
+
+/**
+ * @brief The missing sequence numbers for a session
+ *
+ * This class is used to notify the clients of Logic
+ * the details of state changes.
+ *
+ * Instances of this class is usually used as elements of some containers
+ * such as std::vector, thus it is copyable.
+ */
+class MissingDataInfo
+{
+public:
+ /// @brief session name
+ Name session;
+ /// @brief the lowest one of missing sequence numbers
+ SeqNo low;
+ /// @brief the highest one of missing sequence numbers
+ SeqNo high;
+};
+
+/**
+ * @brief The callback function to handle state updates
+ *
+ * The parameter is a set of MissingDataInfo, of which each corresponds to
+ * a session that has changed its state.
+ */
+typedef function<void(const std::vector<MissingDataInfo>&)> UpdateCallback;
+
+/**
+ * @brief Logic of ChronoSync
+ */
+class Logic : noncopyable
+{
+public:
+ static const time::steady_clock::Duration DEFAULT_RESET_TIMER;
+ static const time::steady_clock::Duration DEFAULT_CANCEL_RESET_TIMER;
+ static const time::milliseconds DEFAULT_RESET_INTEREST_LIFETIME;
+ static const time::milliseconds DEFAULT_SYNC_INTEREST_LIFETIME;
+ static const time::milliseconds DEFAULT_SYNC_REPLY_FRESHNESS;
+
+ /**
+ * @brief Constructor
+ *
+ * @param syncPrefix The prefix of the sync group
+ * @param userPrefix The prefix of the user who owns the session
+ * @param onUpdate The callback function to handle state updates
+ * @param resetTimer The timer to periodically send Reset Interest
+ * @param syncReplyFreshness The FreshnessPeriod of sync reply
+ * @param resetInterestLifetime The lifetime of sync interest
+ * @param resetInterestLifetime The lifetime of Reset Interest
+ * @param cancelResetTimer The timer to exit from Reset state
+ */
+ Logic(ndn::Face& face,
+ const Name& syncPrefix,
+ const Name& userPrefix,
+ const UpdateCallback& onUpdate,
+ const time::steady_clock::Duration& resetTimer = DEFAULT_RESET_TIMER,
+ const time::steady_clock::Duration& cancelResetTimer = DEFAULT_CANCEL_RESET_TIMER,
+ const time::milliseconds& resetInterestLifetime = DEFAULT_RESET_INTEREST_LIFETIME,
+ const time::milliseconds& syncInterestLifetime = DEFAULT_SYNC_INTEREST_LIFETIME,
+ const time::milliseconds& syncReplyFreshness = DEFAULT_SYNC_REPLY_FRESHNESS);
+
+ ~Logic();
+
+ /// @brief Reset the sync tree (and restart synchronization again)
+ void
+ reset();
+
+ /**
+ * @brief Set user prefix
+ *
+ * This method will also change the session name and trigger reset.
+ *
+ * @param userPrefix The prefix of user.
+ */
+ void
+ setUserPrefix(const Name& userPrefix);
+
+ /// @brief Get the name of the local session.
+ const Name&
+ getSessionName() const
+ {
+ return m_sessionName;
+ }
+
+ /// @brief Get current seqNo of the local session.
+ const SeqNo&
+ getSeqNo() const
+ {
+ return m_seqNo;
+ }
+
+ /**
+ * @brief Update the seqNo of the local session
+ *
+ * The method updates the existing seqNo with the supplied seqNo.
+ *
+ * @param seq The new seqNo.
+ */
+ void
+ updateSeqNo(const SeqNo& seq);
+
+ /// @brief Get root digest of current sync tree
+ ndn::ConstBufferPtr
+ getRootDigest() const;
+
+ /// @brief Get the name of all sessions
+ std::set<Name>
+ getSessionNames() const;
+
+CHRONOSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ void
+ printState(std::ostream& os) const;
+
+ ndn::Scheduler&
+ getScheduler()
+ {
+ return m_scheduler;
+ }
+
+ State&
+ getState()
+ {
+ return m_state;
+ }
+
+private:
+ /**
+ * @brief Callback to handle Sync Interest
+ *
+ * This method checks whether an incoming interest is a normal one or a reset
+ * and dispatches the incoming interest to corresponding processing methods.
+ *
+ * @param prefix The prefix of the sync group.
+ * @param interest The incoming sync interest.
+ */
+ void
+ onSyncInterest(const Name& prefix, const Interest& interest);
+
+ /**
+ * @brief Callback to handle Sync prefix registration failure
+ *
+ * This method does nothing for now.
+ *
+ * @param prefix The prefix of the sync group.
+ * @param msg The error message.
+ */
+ void
+ onSyncRegisterFailed(const Name& prefix, const std::string& msg);
+
+ /**
+ * @brief Callback to handle Sync Reply
+ *
+ * This method calls validator to validate Sync Reply.
+ * For now, validation is disabled, Logic::onSyncDataValidated is called
+ * directly.
+ *
+ * @param interest The Sync Interest
+ * @param data The reply to the Sync Interest
+ */
+ void
+ onSyncData(const Interest& interest, Data& data);
+
+ /**
+ * @brief Callback to handle reply to Reset Interest.
+ *
+ * This method does nothing, since reply to Reset Interest is not useful for now.
+ *
+ * @param interest The Reset Interest
+ * @param data The reply to the Reset Interest
+ */
+ void
+ onResetData(const Interest& interest, Data& data);
+
+ /**
+ * @brief Callback to handle Sync Interest timeout.
+ *
+ * This method does nothing, since Logic per se handles timeout explicitly.
+ *
+ * @param interest The Sync Interest
+ */
+ void
+ onSyncTimeout(const Interest& interest);
+
+ /**
+ * @brief Callback to invalid Sync Reply.
+ *
+ * This method does nothing but drops the invalid reply.
+ *
+ * @param data The invalid Sync Reply
+ */
+ void
+ onSyncDataValidationFailed(const shared_ptr<const Data>& data);
+
+ /**
+ * @brief Callback to valid Sync Reply.
+ *
+ * This method simply passes the valid reply to processSyncData.
+ *
+ * @param data The valid Sync Reply.
+ */
+ void
+ onSyncDataValidated(const shared_ptr<const Data>& data);
+
+ /**
+ * @brief Process normal Sync Interest
+ *
+ * This method extracts the digest from the incoming Sync Interest,
+ * compares it against current local digest, and process the Sync
+ * Interest according to the comparison result. See docs/design.rst
+ * for more details.
+ *
+ * @param interest The incoming interest
+ * @param isTimedProcessing True if the interest needs an immediate reply,
+ * otherwise hold the interest for a while before
+ * making a reply (to avoid unnecessary recovery)
+ */
+ void
+ processSyncInterest(const shared_ptr<const Interest>& interest,
+ bool isTimedProcessing = false);
+
+ /**
+ * @brief Process reset Sync Interest
+ *
+ * This method simply call Logic::reset()
+ *
+ * @param interest The incoming interest.
+ */
+ void
+ processResetInterest(const Interest& interest);
+
+ /**
+ * @brief Process Sync Reply.
+ *
+ * This method extracts state update information from Sync Reply and applies
+ * it to the Sync Tree and re-express Sync Interest.
+ *
+ * @param name The data name of the Sync Reply.
+ * @param digest The digest in the data name.
+ * @param syncReplyBlock The content of the Sync Reply.
+ */
+ void
+ processSyncData(const Name& name,
+ ndn::ConstBufferPtr digest,
+ const Block& syncReplyBlock);
+
+ /**
+ * @brief Insert state diff into log
+ *
+ * @param diff The diff .
+ * @param previousRoot The root digest before state changes.
+ */
+ void
+ insertToDiffLog(DiffStatePtr diff,
+ ndn::ConstBufferPtr previousRoot);
+
+ /**
+ * @brief Reply to all pending Sync Interests with a particular commit (or diff)
+ *
+ * @param commit The diff.
+ */
+ void
+ satisfyPendingSyncInterests(ConstDiffStatePtr commit);
+
+ /// @brief Helper method to send normal Sync Interest
+ void
+ sendSyncInterest();
+
+ /// @brief Helper method to send reset Sync Interest
+ void
+ sendResetInterest();
+
+ /// @brief Helper method to send Sync Reply
+ void
+ sendSyncData(const Name& name, const State& state);
+
+ /**
+ * @brief Unset reset status
+ *
+ * By invoking this method, one can add its own state into the Sync Tree, thus
+ * jumping out of the reset status
+ */
+ void
+ cancelReset();
+
+ void
+ printDigest(ndn::ConstBufferPtr digest);
+
+private:
+
+ static const ndn::ConstBufferPtr EMPTY_DIGEST;
+ static const ndn::name::Component RESET_COMPONENT;
+
+ // Communication
+ ndn::Face& m_face;
+ Name m_syncPrefix;
+ const ndn::RegisteredPrefixId* m_syncRegisteredPrefixId;
+ Name m_syncReset;
+ Name m_userPrefix;
+
+ // State
+ Name m_sessionName;
+ SeqNo m_seqNo;
+ State m_state;
+ DiffStateContainer m_log;
+ InterestTable m_interestTable;
+ Name m_outstandingInterestName;
+ const ndn::PendingInterestId* m_outstandingInterestId;
+ bool m_isInReset;
+ bool m_needPeriodReset;
+
+ // Callback
+ UpdateCallback m_onUpdate;
+
+ // Event
+ ndn::Scheduler m_scheduler;
+ ndn::EventId m_delayedInterestProcessingId;
+ ndn::EventId m_reexpressingInterestId;
+ ndn::EventId m_resetInterestId;
+
+ // Timer
+ boost::mt19937 m_randomGenerator;
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<> > m_rangeUniformRandom;
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<> > m_reexpressionJitter;
+ /// @brief Timer to send next reset 0 for no reset
+ time::steady_clock::Duration m_resetTimer;
+ /// @brief Timer to cancel reset state
+ time::steady_clock::Duration m_cancelResetTimer;
+ /// @brief Lifetime of reset interest
+ time::milliseconds m_resetInterestLifetime;
+ /// @brief Lifetime of sync interest
+ time::milliseconds m_syncInterestLifetime;
+ /// @brief FreshnessPeriod of SyncReply
+ time::milliseconds m_syncReplyFreshness;
+
+ // Others
+ ndn::KeyChain m_keyChain;
+
+#ifdef _DEBUG
+ int m_instanceId;
+ static int m_instanceCounter;
+#endif
+};
+
+
+} // namespace chronosync
+
+#endif // CHRONOSYNC_LOGIC_HPP
diff --git a/src/sync-name-info.cc b/src/mi-tag.hpp
similarity index 78%
copy from src/sync-name-info.cc
copy to src/mi-tag.hpp
index eb7411a..1abafc4 100644
--- a/src/sync-name-info.cc
+++ b/src/mi-tag.hpp
@@ -19,15 +19,32 @@
* @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
* @author Chaoyi Bian <bcy@pku.edu.cn>
* @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
*/
-#include "sync-name-info.h"
+#ifndef CHRONOSYNC_MI_TAG_HPP
+#define CHRONOSYNC_MI_TAG_HPP
-// #include <boost/lexical_cast.hpp>
+namespace chronosync {
-namespace Sync {
+// Multi-Index-Container Tags
+struct hashed
+{
+};
-NameInfo::NameMap NameInfo::m_names;
-size_t NameInfo::m_ids = 0;
+struct ordered
+{
+};
-} // Sync
+struct sequenced
+{
+};
+
+struct timed
+{
+};
+
+
+} // namespace chronosync
+
+#endif // CHRONOSYNC_MI_TAG_HPP
diff --git a/src/socket.cpp b/src/socket.cpp
new file mode 100644
index 0000000..55296ab
--- /dev/null
+++ b/src/socket.cpp
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#include "socket.hpp"
+#include "logger.hpp"
+
+INIT_LOGGER("Socket");
+
+
+namespace chronosync {
+
+Socket::Socket(const Name& syncPrefix,
+ const Name& userPrefix,
+ ndn::Face& face,
+ const UpdateCallback& updateCallback)
+ : m_userPrefix(userPrefix)
+ , m_face(face)
+ , m_logic(face,
+ syncPrefix,
+ userPrefix,
+ updateCallback)
+{
+}
+
+
+void
+Socket::publishData(const uint8_t* buf, size_t len, const ndn::time::milliseconds& freshness)
+{
+ publishData(ndn::dataBlock(ndn::tlv::Content, buf, len), freshness);
+}
+
+void
+Socket::publishData(const Block& content, const ndn::time::milliseconds& freshness)
+{
+ shared_ptr<Data> data = make_shared<Data>();
+ data->setContent(content);
+ data->setFreshnessPeriod(freshness);
+
+ SeqNo newSeq = m_logic.getSeqNo() + 1;
+ Name dataName;
+ dataName.append(m_logic.getSessionName()).appendNumber(newSeq);
+ data->setName(dataName);
+
+ m_keyChain.sign(*data);
+
+ m_face.put(*data);
+
+ m_logic.updateSeqNo(newSeq);
+}
+
+void
+Socket::fetchData(const Name& sessionName, const SeqNo& seqNo,
+ const ndn::OnDataValidated& dataCallback,
+ int nRetries)
+{
+ Name interestName;
+ interestName.append(sessionName).appendNumber(seqNo);
+
+ Interest interest(interestName);
+ interest.setMustBeFresh(true);
+
+ ndn::OnDataValidationFailed failureCallback =
+ bind(&Socket::onDataValidationFailed, this, _1, _2);
+
+ m_face.expressInterest(interest,
+ bind(&Socket::onData, this, _1, _2, dataCallback, failureCallback),
+ bind(&Socket::onDataTimeout, this, _1, nRetries,
+ dataCallback, failureCallback));
+}
+
+void
+Socket::fetchData(const Name& sessionName, const SeqNo& seqNo,
+ const ndn::OnDataValidated& dataCallback,
+ const ndn::OnDataValidationFailed& failureCallback,
+ const ndn::OnTimeout& onTimeout,
+ int nRetries)
+{
+ _LOG_DEBUG(">> Socket::fetchData");
+ Name interestName;
+ interestName.append(sessionName).appendNumber(seqNo);
+
+ Interest interest(interestName);
+ interest.setMustBeFresh(true);
+
+ m_face.expressInterest(interest,
+ bind(&Socket::onData, this, _1, _2, dataCallback, failureCallback),
+ onTimeout);
+
+ _LOG_DEBUG("<< Socket::fetchData");
+}
+
+void
+Socket::onData(const Interest& interest, Data& data,
+ const ndn::OnDataValidated& onValidated,
+ const ndn::OnDataValidationFailed& onFailed)
+{
+ _LOG_DEBUG("Socket::onData");
+ // Placeholder for validator
+ onValidated(data.shared_from_this());
+}
+
+void
+Socket::onDataTimeout(const Interest& interest, int nRetries,
+ const ndn::OnDataValidated& onValidated,
+ const ndn::OnDataValidationFailed& onFailed)
+{
+ _LOG_DEBUG("Socket::onDataTimeout");
+ if (nRetries <= 0)
+ return;
+
+ m_face.expressInterest(interest,
+ bind(&Socket::onData, this, _1, _2, onValidated, onFailed),
+ bind(&Socket::onDataTimeout, this, _1, nRetries - 1,
+ onValidated, onFailed));
+}
+
+void
+Socket::onDataValidationFailed(const shared_ptr<const Data>& data,
+ const std::string& failureInfo)
+{
+}
+
+ndn::ConstBufferPtr
+Socket::getRootDigest() const
+{
+ return m_logic.getRootDigest();
+}
+
+} // namespace chronosync
diff --git a/src/socket.hpp b/src/socket.hpp
new file mode 100644
index 0000000..11dc684
--- /dev/null
+++ b/src/socket.hpp
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOSYNC_SOCKET_HPP
+#define CHRONOSYNC_SOCKET_HPP
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/security/validation-request.hpp>
+
+#include "logic.hpp"
+
+namespace chronosync {
+
+/**
+ * @brief A simple interface to interact with client code
+ *
+ * Though it is called Socket, it is not a real socket. It just trying to provide
+ * a simplified interface for data publishing and fetching.
+ *
+ * This interface simplify data publishing. Client can simply dump raw data
+ * into this interface without handling the ChronoSync specific details, such
+ * as sequence number and session id.
+ *
+ * This interface also simplify data fetching. Client only needs to provide a
+ * data fetching strategy (through a updateCallback).
+ */
+class Socket : noncopyable
+{
+public:
+ class Error : public std::runtime_error
+ {
+ public:
+ explicit
+ Error(const std::string& what)
+ : std::runtime_error(what)
+ {
+ }
+ };
+
+ Socket(const Name& syncPrefix,
+ const Name& userPrefix,
+ ndn::Face& face,
+ const UpdateCallback& updateCallback);
+
+ /**
+ * @brief Publish a data packet in the session and trigger synchronization updates
+ *
+ * This method will create a data packet with the supplied content.
+ * The packet name is the local session + seqNo.
+ * The seqNo is automatically maintained by internal Logic.
+ *
+ * @param buf Pointer to the bytes in content
+ * @param len size of the bytes in content
+ * @param freshness FreshnessPeriod of the data packet.
+ */
+ void
+ publishData(const uint8_t* buf, size_t len, const ndn::time::milliseconds& freshness);
+
+ /**
+ * @brief Publish a data packet in the session and trigger synchronization updates
+ *
+ * This method will create a data packet with the supplied content.
+ * The packet name is the local session + seqNo.
+ * The seqNo is automatically maintained by internal Logic.
+ *
+ * @param content Block that will be set as the content of the data packet.
+ * @param freshness FreshnessPeriod of the data packet.
+ */
+ void
+ publishData(const Block& content, const ndn::time::milliseconds& freshness);
+
+ /**
+ * @brief Retrive a data packet with a particular seqNo from a session
+ *
+ * @param sessionName The name of the target session.
+ * @param seq The seqNo of the data packet.
+ * @param onValidated The callback when the retrieved packet has been validated.
+ * @param nRetries The number of retries.
+ */
+ void
+ fetchData(const Name& sessionName, const SeqNo& seq,
+ const ndn::OnDataValidated& onValidated,
+ int nRetries = 0);
+
+ /**
+ * @brief Retrive a data packet with a particular seqNo from a session
+ *
+ * @param sessionName The name of the target session.
+ * @param seq The seqNo of the data packet.
+ * @param onValidated The callback when the retrieved packet has been validated.
+ * @param nRetries The number of retries.
+ */
+ void
+ fetchData(const Name& sessionName, const SeqNo& seq,
+ const ndn::OnDataValidated& onValidated,
+ const ndn::OnDataValidationFailed& onValidationFailed,
+ const ndn::OnTimeout& onTimeout,
+ int nRetries = 0);
+
+ /// @brief Get the root digest of current sync tree
+ ndn::ConstBufferPtr
+ getRootDigest() const;
+
+ Logic&
+ getLogic()
+ {
+ return m_logic;
+ }
+
+private:
+ void
+ onData(const Interest& interest, Data& data,
+ const ndn::OnDataValidated& dataCallback,
+ const ndn::OnDataValidationFailed& failCallback);
+
+ void
+ onDataTimeout(const Interest& interest, int nRetries,
+ const ndn::OnDataValidated& dataCallback,
+ const ndn::OnDataValidationFailed& failCallback);
+
+ void
+ onDataValidationFailed(const shared_ptr<const Data>& data,
+ const std::string& failureInfo);
+
+private:
+
+ Name m_userPrefix;
+ ndn::Face& m_face;
+
+ Logic m_logic;
+
+ ndn::KeyChain m_keyChain;
+};
+
+} // namespace chronosync
+
+#endif // CHRONOSYNC_SOCKET_HPP
diff --git a/src/state.cpp b/src/state.cpp
new file mode 100644
index 0000000..251d46b
--- /dev/null
+++ b/src/state.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#include "state.hpp"
+
+namespace chronosync {
+
+using boost::make_tuple;
+
+State::~State()
+{
+}
+
+/**
+ * @brief Add or update leaf to the sync tree
+ *
+ * @param info session name of the leaf
+ * @param seq sequence number of the leaf
+ * @return 3-tuple (isInserted, isUpdated, oldSeqNo)
+ */
+boost::tuple<bool, bool, SeqNo>
+State::update(const Name& info, const SeqNo& seq)
+{
+ m_wire.reset();
+
+ LeafContainer::iterator leaf = m_leaves.find(info);
+
+ if (leaf == m_leaves.end()) {
+ m_leaves.insert(make_shared<Leaf>(info, cref(seq)));
+ return make_tuple(true, false, 0);
+ }
+ else {
+ if ((*leaf)->getSeq() == seq || seq < (*leaf)->getSeq()) {
+ return make_tuple(false, false, 0);
+ }
+
+ SeqNo old = (*leaf)->getSeq();
+ m_leaves.modify(leaf,
+ [=] (LeafPtr& leaf) { leaf->setSeq(seq); } );
+ return make_tuple(false, true, old);
+ }
+}
+
+ndn::ConstBufferPtr
+State::getRootDigest() const
+{
+ m_digest.reset();
+
+ BOOST_FOREACH (ConstLeafPtr leaf, m_leaves.get<ordered>())
+ {
+ BOOST_ASSERT(leaf != 0);
+ m_digest.update(leaf->getDigest()->buf(), leaf->getDigest()->size());
+ }
+
+ return m_digest.computeDigest();
+}
+
+
+void
+State::reset()
+{
+ m_leaves.clear();
+}
+
+State&
+State::operator+=(const State& state)
+{
+ BOOST_FOREACH (ConstLeafPtr leaf, state.getLeaves())
+ {
+ BOOST_ASSERT(leaf != 0);
+ update(leaf->getSessionName(), leaf->getSeq());
+ }
+
+ return *this;
+}
+
+template<bool T>
+size_t
+State::wireEncode(ndn::EncodingImpl<T>& block) const
+{
+ size_t totalLength = 0;
+
+ BOOST_REVERSE_FOREACH (ConstLeafPtr leaf, m_leaves.get<ordered>())
+ {
+ size_t entryLength = 0;
+ entryLength += prependNonNegativeIntegerBlock(block, tlv::SeqNo, leaf->getSeq());
+ entryLength += leaf->getSessionName().wireEncode(block);
+ entryLength += block.prependVarNumber(entryLength);
+ entryLength += block.prependVarNumber(tlv::StateLeaf);
+ totalLength += entryLength;
+ }
+
+ totalLength += block.prependVarNumber(totalLength);
+ totalLength += block.prependVarNumber(tlv::SyncReply);
+
+ return totalLength;
+}
+
+template size_t
+State::wireEncode<true>(ndn::EncodingImpl<true>& block) const;
+
+template size_t
+State::wireEncode<false>(ndn::EncodingImpl<false>& block) const;
+
+const Block&
+State::wireEncode() const
+{
+ if (m_wire.hasWire())
+ return m_wire;
+
+ ndn::EncodingEstimator estimator;
+ size_t estimatedSize = wireEncode(estimator);
+
+ ndn::EncodingBuffer buffer(estimatedSize, 0);
+ wireEncode(buffer);
+
+ m_wire = buffer.block();
+ return m_wire;
+}
+
+void
+State::wireDecode(const Block& wire)
+{
+ if (!wire.hasWire())
+ throw Error("The supplied block does not contain wire format");
+
+ if (wire.type() != tlv::SyncReply)
+ throw Error("Unexpected TLV type when decoding SyncReply: " +
+ boost::lexical_cast<std::string>(m_wire.type()));
+
+ wire.parse();
+ m_wire = wire;
+
+ for (Block::element_const_iterator it = wire.elements_begin();
+ it != wire.elements_end(); it++) {
+ if (it->type() == tlv::StateLeaf) {
+ it->parse();
+
+ Block::element_const_iterator val = it->elements_begin();
+ Name info(*val);
+ val++;
+
+ if (val != it->elements_end())
+ update(info, readNonNegativeInteger(*val));
+ else
+ throw Error("No seqNo when decoding SyncReply");
+ }
+ }
+}
+
+} // namespace chronosync
diff --git a/src/state.hpp b/src/state.hpp
new file mode 100644
index 0000000..4fa82e9
--- /dev/null
+++ b/src/state.hpp
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
+ * @author Chaoyi Bian <bcy@pku.edu.cn>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOSYNC_STATE_HPP
+#define CHRONOSYNC_STATE_HPP
+
+#include "tlv.hpp"
+#include "leaf-container.hpp"
+#include <ndn-cxx/util/digest.hpp>
+
+namespace chronosync {
+
+class State;
+typedef shared_ptr<State> StatePtr;
+typedef shared_ptr<const State> ConstStatePtr;
+
+/**
+ * @brief Abstraction of state tree.
+ *
+ * State is used to represent sync tree, it is also the base class of DiffState,
+ * which represent the diff between two states. Due to the second usage, State
+ * should be copyable.
+ */
+class State
+{
+public:
+ class Error : public std::runtime_error
+ {
+ public:
+ explicit
+ Error(const std::string& what)
+ : std::runtime_error(what)
+ {
+ }
+ };
+
+ virtual
+ ~State();
+
+ /**
+ * @brief Add or update leaf to the sync tree
+ *
+ * @param info session name of the leaf
+ * @param seq sequence number of the leaf
+ * @return 3-tuple (isInserted, isUpdated, oldSeqNo)
+ */
+ boost::tuple<bool, bool, SeqNo>
+ update(const Name& info, const SeqNo& seq);
+
+ /**
+ * @brief Get state leaves
+ */
+ const LeafContainer&
+ getLeaves() const
+ {
+ return m_leaves;
+ }
+
+ ndn::ConstBufferPtr
+ getRootDigest() const;
+
+ /**
+ * @brief Reset the sync tree, remove all state leaves
+ */
+ void
+ reset();
+
+ /**
+ * @brief Combine `this' state and the supplied state
+ *
+ * The combination result contains all leaves in two states.
+ * When leaves conflict, keep the one with largest seq.
+ *
+ * @param state another state to combine with
+ * @return Combined state
+ */
+ State&
+ operator+=(const State& state);
+
+ /**
+ * @brief Encode to a wire format
+ */
+ const Block&
+ wireEncode() const;
+
+ /**
+ * @brief Decode from the wire format
+ */
+ void
+ wireDecode(const Block& wire);
+
+protected:
+ template<bool T>
+ size_t
+ wireEncode(ndn::EncodingImpl<T>& block) const;
+
+protected:
+ LeafContainer m_leaves;
+
+ mutable ndn::util::Sha256 m_digest;
+ mutable Block m_wire;
+};
+
+} // namespace chronosync
+
+#endif // CHRONOSYNC_STATE_HPP
diff --git a/src/sync-diff-leaf.cc b/src/sync-diff-leaf.cc
deleted file mode 100644
index 80527d6..0000000
--- a/src/sync-diff-leaf.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-diff-leaf.h"
-#include <boost/throw_exception.hpp>
-typedef boost::error_info<struct tag_errmsg, std::string> errmsg_info;
-
-using namespace Sync::Error;
-
-namespace Sync {
-
-DiffLeaf::DiffLeaf (NameInfoConstPtr info, const SeqNo &seq)
- : Leaf (info, seq)
- , m_op (UPDATE)
-{
-}
-
-DiffLeaf::DiffLeaf (NameInfoConstPtr info)
- : Leaf (info, SeqNo (0,0))
- , m_op (REMOVE)
-{
-}
-
-std::ostream &
-operator << (std::ostream &os, Operation op)
-{
- switch (op)
- {
- case UPDATE:
- os << "update";
- break;
- case REMOVE:
- os << "remove";
- break;
- }
- return os;
-}
-
-std::istream &
-operator >> (std::istream &is, Operation &op)
-{
- std::string operation;
- is >> operation;
- if (operation == "update")
- op = UPDATE;
- else if (operation == "remove")
- op = REMOVE;
- else
- BOOST_THROW_EXCEPTION (SyncDiffLeafOperationParseError () << errmsg_info (operation));
-
- return is;
-}
-
-
-}
diff --git a/src/sync-diff-leaf.h b/src/sync-diff-leaf.h
deleted file mode 100644
index 2f692ee..0000000
--- a/src/sync-diff-leaf.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_DIFF_LEAF_H
-#define SYNC_DIFF_LEAF_H
-
-#include "sync-leaf.h"
-#include <boost/exception/all.hpp>
-
-namespace Sync {
-
-/**
- * @ingroup sync
- * @brief Annotation for SYNC leaf
- */
-enum Operation
- {
- UPDATE, ///< @brief Leaf was added or updated
- REMOVE ///< @brief Leaf was removed
- };
-
-/**
- * @ingroup sync
- * @brief Annotated SYNC leaf
- */
-class DiffLeaf : public Leaf
-{
-public:
- /**
- * @brief Constructor to create an UPDATE diff leaf
- * @param info Smart pointer to leaf's name
- * @param seq Initial sequence number of the pointer
- */
- DiffLeaf (NameInfoConstPtr info, const SeqNo &seq);
-
- /**
- * @brief Constructor to create an REMOVE diff leaf
- * @param info Smart pointer to leaf's name
- *
- * This constructor creates a leaf with phony sequence number
- * with 0 session ID and 0 sequence number
- */
- DiffLeaf (NameInfoConstPtr info);
-
- virtual ~DiffLeaf () { }
-
- /**
- * @brief Get diff leaf type
- */
- Operation
- getOperation () const { return m_op; }
-
-private:
- Operation m_op;
-};
-
-typedef boost::shared_ptr<DiffLeaf> DiffLeafPtr;
-typedef boost::shared_ptr<const DiffLeaf> DiffLeafConstPtr;
-
-std::ostream &
-operator << (std::ostream &os, Operation op);
-
-std::istream &
-operator >> (std::istream &is, Operation &op);
-
-namespace Error {
-struct SyncDiffLeafOperationParseError : virtual boost::exception, virtual std::exception { };
-} // Error
-
-} // Sync
-
-#endif // SYNC_DIFF_LEAF_H
diff --git a/src/sync-diff-state.cc b/src/sync-diff-state.cc
deleted file mode 100644
index 5ddaace..0000000
--- a/src/sync-diff-state.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-diff-state.h"
-#include "sync-diff-leaf.h"
-
-#include <boost/make_shared.hpp>
-#include <boost/foreach.hpp>
-#include <boost/assert.hpp>
-
-using namespace boost;
-
-namespace Sync {
-
-DiffState::DiffState ()
-{
-}
-
-DiffState::~DiffState ()
-{
-}
-
-DiffStatePtr
-DiffState::diff () const
-{
- DiffStatePtr ret = make_shared<DiffState> ();
-
- DiffStatePtr state = m_next;
- while (state != 0)
- {
- *ret += *state;
- state = state->m_next;
- }
-
- return ret;
-}
-
-DiffState &
-DiffState::operator += (const DiffState &state)
-{
- BOOST_FOREACH (LeafConstPtr _leaf, state.getLeaves ())
- {
- DiffLeafConstPtr leaf = dynamic_pointer_cast<const DiffLeaf> (_leaf);
- BOOST_ASSERT (leaf != 0);
-
- if (leaf->getOperation () == UPDATE)
- update (leaf->getInfo (), leaf->getSeq ());
- else if (leaf->getOperation () == REMOVE)
- remove (leaf->getInfo ());
- else
- {
- BOOST_ASSERT (false);
- }
- }
-
- return *this;
-}
-
-// from State
-boost::tuple<bool/*inserted*/, bool/*updated*/, SeqNo/*oldSeqNo*/>
-DiffState::update (NameInfoConstPtr info, const SeqNo &seq)
-{
- m_leaves.erase (info);
-
- DiffLeafPtr leaf = make_shared<DiffLeaf> (info, cref (seq));
- m_leaves.insert (leaf);
-
- return make_tuple (true, false, SeqNo ());
-}
-
-bool
-DiffState::remove (NameInfoConstPtr info)
-{
- m_leaves.erase (info);
-
- DiffLeafPtr leaf = make_shared<DiffLeaf> (info);
- m_leaves.insert (leaf);
-
- return true;
-}
-
-} // ns3
diff --git a/src/sync-diff-state.h b/src/sync-diff-state.h
deleted file mode 100644
index da0d649..0000000
--- a/src/sync-diff-state.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_DIFF_STATE_H
-#define SYNC_DIFF_STATE_H
-
-#include "sync-state.h"
-#include <iostream>
-
-namespace Sync {
-
-class DiffState;
-typedef boost::shared_ptr<DiffState> DiffStatePtr;
-typedef boost::shared_ptr<DiffState> DiffStateConstPtr;
-
-/**
- * @ingroup ccnx
- * @brief Differential SYNC state
- */
-class DiffState : public State
-{
-public:
- /**
- * @see Default constructor
- */
- DiffState ();
- virtual ~DiffState ();
-
- /**
- * @brief Set successor for the diff state
- * @param next successor state
- */
- void
- setNext (DiffStatePtr next)
- {
- m_next = next;
- }
-
- /**
- * @brief Set digest for the diff state (obtained from a corresponding full state)
- * @param digest A read only smart pointer to a digest object (that should be unmodified anywhere else)
- */
- void
- setDigest (DigestConstPtr digest) { m_digest = digest; }
-
- /**
- * @brief Get digest for the diff state
- */
- DigestConstPtr
- getDigest () const { return m_digest; }
-
- /**
- * @brief Accumulate differences from `this' state to the most current state
- * @returns Accumulated differences from `this' state to the most current state
- */
- DiffStatePtr
- diff () const;
-
- /**
- * @brief Combine differences from `this' and `state'
- * @param state Differential state to combine with
- * @return Combined differences
- *
- * In case of collisions, `this' leaf will be replaced with the leaf of `state'
- */
- DiffState&
- operator += (const DiffState &state);
-
- // from State
- virtual boost::tuple<bool/*inserted*/, bool/*updated*/, SeqNo/*oldSeqNo*/>
- update (NameInfoConstPtr info, const SeqNo &seq);
-
- virtual bool
- remove (NameInfoConstPtr info);
-
-private:
- DiffStatePtr m_next;
- DigestConstPtr m_digest;
-};
-
-} // Sync
-
-#endif // SYNC_DIFF_STATE_H
diff --git a/src/sync-digest.cc b/src/sync-digest.cc
deleted file mode 100644
index ce1d506..0000000
--- a/src/sync-digest.cc
+++ /dev/null
@@ -1,262 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-digest.h"
-#include <string.h>
-
-#include "boost-header.h"
-
-typedef boost::error_info<struct tag_errmsg, std::string> errmsg_info_str;
-typedef boost::error_info<struct tag_errmsg, int> errmsg_info_int;
-
-// for printing, may be disabled in optimized build
-
-// #ifdef DIGEST_BASE64
-// #include <boost/archive/iterators/base64_from_binary.hpp>
-// #include <boost/archive/iterators/binary_from_base64.hpp>
-// #endif
-
-
-
-using namespace boost;
-using namespace boost::archive::iterators;
-
-// Other options: VP_md2, EVP_md5, EVP_sha, EVP_sha1, EVP_sha256, EVP_dss, EVP_dss1, EVP_mdc2, EVP_ripemd160
-#define HASH_FUNCTION EVP_sha256
-#define HASH_FUNCTION_LEN 32
-
-
-// #ifndef DIGEST_BASE64
-
-template<class CharType>
-struct hex_from_4_bit
-{
- typedef CharType result_type;
- CharType operator () (CharType ch) const
- {
- const char *lookup_table = "0123456789abcdef";
- // cout << "New character: " << (int) ch << " (" << (char) ch << ")" << "\n";
- BOOST_ASSERT (ch < 16);
- return lookup_table[static_cast<size_t>(ch)];
- }
-};
-
-typedef transform_iterator<hex_from_4_bit<std::vector<uint8_t>::const_iterator::value_type>,
- transform_width<std::vector<uint8_t>::const_iterator, 4, 8, std::vector<uint8_t>::const_iterator::value_type> > string_from_binary;
-
-
-template<class CharType>
-struct hex_to_4_bit
-{
- typedef CharType result_type;
- CharType operator () (CharType ch) const
- {
- const signed char lookup_table [] = {
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
- -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- };
-
- // cout << "New character: " << hex << (int) ch << " (" << (char) ch << ")" << "\n";
- signed char value = -1;
- if ((unsigned)ch < 128)
- value = lookup_table [(unsigned)ch];
- if (value == -1)
- BOOST_THROW_EXCEPTION (Sync::Error::DigestCalculationError () << errmsg_info_int ((int)ch));
-
- return value;
- }
-};
-
-typedef transform_width<transform_iterator<hex_to_4_bit<std::string::const_iterator::value_type>, std::string::const_iterator>, 8, 4> string_to_binary;
-
-namespace Sync {
-
-Digest::Digest ()
-{
- m_context = EVP_MD_CTX_create ();
-
- reset ();
-}
-
-Digest::~Digest ()
-{
- EVP_MD_CTX_destroy (m_context);
-}
-
-bool
-Digest::empty () const
-{
- return m_buffer.empty ();
-}
-
-bool
-Digest::isZero () const
-{
- if (m_buffer.empty ())
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("Digest has not been yet finalized"));
-
- return (m_buffer.size () == 1 && m_buffer[0] == 0);
-}
-
-
-void
-Digest::reset ()
-{
- m_buffer.clear ();
-
- int ok = EVP_DigestInit_ex (m_context, HASH_FUNCTION (), 0);
- if (!ok)
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("EVP_DigestInit_ex returned error")
- << errmsg_info_int (ok));
-}
-
-
-void
-Digest::finalize ()
-{
- if (!m_buffer.empty ()) return;
-
- m_buffer.resize (HASH_FUNCTION_LEN);
-
- unsigned int tmp;
- int ok = EVP_DigestFinal_ex (m_context,
- &m_buffer[0], &tmp);
- if (!ok)
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("EVP_DigestFinal_ex returned error")
- << errmsg_info_int (ok));
-}
-
-std::size_t
-Digest::getHash () const
-{
- if (isZero ()) return 0;
-
- if (sizeof (std::size_t) > m_buffer.size ())
- {
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("Hash is not zero and length is less than size_t")
- << errmsg_info_int (m_buffer.size ()));
- }
-
- // just getting first sizeof(std::size_t) bytes
- // not ideal, but should work pretty well
- return *(reinterpret_cast<const std::size_t*> (&m_buffer[0]));
-}
-
-bool
-Digest::operator == (const Digest &digest) const
-{
- if (m_buffer.empty ())
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("Digest1 is empty"));
-
- if (digest.m_buffer.empty ())
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("Digest2 is empty"));
-
- return m_buffer == digest.m_buffer;
-}
-
-
-void
-Digest::update (const uint8_t *buffer, size_t size)
-{
- // cout << "Update: " << (void*)buffer << " / size: " << size << "\n";
-
- // cannot update Digest when it has been finalized
- if (!m_buffer.empty ())
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("Digest has been already finalized"));
-
- bool ok = EVP_DigestUpdate (m_context, buffer, size);
- if (!ok)
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("EVP_DigestUpdate returned error")
- << errmsg_info_int (ok));
-}
-
-
-Digest &
-Digest::operator << (const Digest &src)
-{
- if (src.m_buffer.empty ())
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("Digest has not been yet finalized"));
-
- update (&src.m_buffer[0], src.m_buffer.size ());
-
- return *this;
-}
-
-std::ostream &
-operator << (std::ostream &os, const Digest &digest)
-{
- BOOST_ASSERT (!digest.m_buffer.empty ());
-
- std::ostreambuf_iterator<char> out_it (os); // ostream iterator
- // need to encode to base64
- copy (string_from_binary (digest.m_buffer.begin ()),
- string_from_binary (digest.m_buffer.end ()),
- out_it);
-
- return os;
-}
-
-std::istream &
-operator >> (std::istream &is, Digest &digest)
-{
- std::string str;
- is >> str; // read string first
-
- if (str.size () == 0)
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("Input is empty"));
-
- // uint8_t padding = (3 - str.size () % 3) % 3;
- // for (uint8_t i = 0; i < padding; i++) str.push_back ('=');
-
- // only empty digest object can be used for reading
- if (!digest.m_buffer.empty ())
- BOOST_THROW_EXCEPTION (Error::DigestCalculationError ()
- << errmsg_info_str ("Digest has been already finalized"));
-
- digest.m_buffer.clear ();
-
- copy (string_to_binary (str.begin ()),
- string_to_binary (str.end ()),
- std::back_inserter (digest.m_buffer));
-
- return is;
-}
-
-
-} // Sync
diff --git a/src/sync-digest.h b/src/sync-digest.h
deleted file mode 100644
index d66af3e..0000000
--- a/src/sync-digest.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_DIGEST_H
-#define SYNC_DIGEST_H
-
-#include <boost/exception/all.hpp>
-#include <openssl/evp.h>
-#include <boost/cstdint.hpp>
-#include <vector>
-
-namespace Sync {
-
-/**
- * @ingroup sync
- * @brief A simple wrapper for libcrypto hash functions
- */
-class Digest
-{
-public:
- /**
- * @brief Default constructor. Will initialize internal libssl structures
- */
- Digest ();
-
- /**
- * @brief Check if digest is empty
- */
- bool
- empty () const;
-
- /**
- * @brief Reset digest to the initial state
- */
- void
- reset ();
-
- /**
- * @brief Destructor
- */
- ~Digest ();
-
- /**
- * @brief Obtain a short version of the hash (just first sizeof(size_t) bytes
- *
- * Side effect: finalize() will be called on `this'
- */
- std::size_t
- getHash () const;
-
- /**
- * @brief Finalize digest. All subsequent calls to "operator <<" will fire an exception
- */
- void
- finalize ();
-
- /**
- * @brief Compare two full digests
- *
- * Side effect: Finalize will be called on `this' and `digest'
- */
- bool
- operator == (const Digest &digest) const;
-
- bool
- operator != (const Digest &digest) const
- { return ! (*this == digest); }
-
-
- /**
- * @brief Add existing digest to digest calculation
- * @param src digest to combine with
- *
- * The result of this combination is hash (hash (...))
- */
- Digest &
- operator << (const Digest &src);
-
- /**
- * @brief Add string to digest calculation
- * @param str string to put into digest
- */
- inline Digest &
- operator << (const std::string &str);
-
- /**
- * @brief Add uint64_t value to digest calculation
- * @param value uint64_t value to put into digest
- */
- inline Digest &
- operator << (uint64_t value);
-
- /**
- * @brief Checks if the stored hash is zero-root hash
- *
- * Zero-root hash is a valid hash that optimally represents an empty state
- */
- bool
- isZero () const;
-
-private:
- Digest &
- operator = (Digest &digest) { (void)digest; return *this; }
-
- /**
- * @brief Add size bytes of buffer to the hash
- */
- void
- update (const uint8_t *buffer, size_t size);
-
- friend std::ostream &
- operator << (std::ostream &os, const Digest &digest);
-
- friend std::istream &
- operator >> (std::istream &is, Digest &digest);
-
-private:
- EVP_MD_CTX *m_context;
- std::vector<uint8_t> m_buffer;
-};
-
-namespace Error {
-struct DigestCalculationError : virtual boost::exception, virtual std::exception { };
-}
-
-typedef boost::shared_ptr<Digest> DigestPtr;
-typedef boost::shared_ptr<const Digest> DigestConstPtr;
-
-Digest &
-Digest::operator << (const std::string &str)
-{
- update (reinterpret_cast<const uint8_t*> (str.c_str ()), str.size ());
- return *this;
-}
-
-inline Digest &
-Digest::operator << (uint64_t value)
-{
- update (reinterpret_cast<const uint8_t*> (&value), sizeof (uint64_t));
- return *this;
-}
-
-std::ostream &
-operator << (std::ostream &os, const Digest &digest);
-
-std::istream &
-operator >> (std::istream &is, Digest &digest);
-
-// template<class INT>
-// Digest &
-// Digest::operator << (INT value)
-// {
-// update (&value, sizeof (INT));
-// return *this;
-// }
-
-struct DigestPtrHash : public std::unary_function<Digest, std::size_t>
-{
- std::size_t
- operator() (DigestConstPtr digest) const
- {
- // std::cout << "digest->getHash: " << digest->getHash () << " (" << *digest << ")" << std::endl;
- return digest->getHash ();
- }
-};
-
-struct DigestPtrEqual : public std::unary_function<Digest, std::size_t>
-{
- bool
- operator() (DigestConstPtr digest1, DigestConstPtr digest2) const
- {
- // std::cout << boost::cref(*digest1) << " == " << boost::cref(*digest2) << " : " << (*digest1 == *digest2) << std::endl;
- return *digest1 == *digest2;
- }
-};
-
-
-} // Sync
-
-#endif // SYNC_DIGEST_H
diff --git a/src/sync-event.h b/src/sync-event.h
deleted file mode 100644
index bbe07bc..0000000
--- a/src/sync-event.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_EVENT_H
-#define SYNC_EVENT_H
-
-#include <boost/function.hpp>
-
-namespace Sync
-{
-
-typedef boost::function< void ( ) > Event;
-
-} // Sync
-
-#endif // SYNC_EVENT_H
diff --git a/src/sync-full-leaf.cc b/src/sync-full-leaf.cc
deleted file mode 100644
index 7a25c8b..0000000
--- a/src/sync-full-leaf.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-full-leaf.h"
-#include <boost/ref.hpp>
-
-using namespace boost;
-
-namespace Sync {
-
-FullLeaf::FullLeaf (NameInfoConstPtr info, const SeqNo &seq)
- : Leaf (info, seq)
-{
- updateDigest ();
-}
-
-void
-FullLeaf::updateDigest ()
-{
- m_digest.reset ();
- m_digest << getInfo ()->getDigest () << *getSeq ().getDigest ();
- m_digest.finalize ();
-}
-
-// from Leaf
-void
-FullLeaf::setSeq (const SeqNo &seq)
-{
- Leaf::setSeq (seq);
- updateDigest ();
-}
-
-} // Sync
diff --git a/src/sync-full-leaf.h b/src/sync-full-leaf.h
deleted file mode 100644
index 5e7f8e1..0000000
--- a/src/sync-full-leaf.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_FULL_LEAF_H
-#define SYNC_FULL_LEAF_H
-
-#include "sync-leaf.h"
-
-namespace Sync {
-
-/**
- * @ingroup sync
- * @brief SYNC leaf for the full state (with support of Digest calculation)
- */
-class FullLeaf : public Leaf
-{
-public:
- /**
- * @brief Constructor to create an UPDATE diff leaf
- * @param info Smart pointer to leaf's name
- * @param seq Initial sequence number of the pointer
- */
- FullLeaf (NameInfoConstPtr info, const SeqNo &seq);
- virtual ~FullLeaf () { }
-
- /**
- * @brief Get hash digest of the leaf
- *
- * The underlying Digest object is recalculated on every update or removal
- * (including updates of child classes)
- */
- const Digest &
- getDigest () const { return m_digest; }
-
- // from Leaf
- virtual void
- setSeq (const SeqNo &seq);
-
-private:
- void
- updateDigest ();
-
-private:
- Digest m_digest;
-};
-
-typedef boost::shared_ptr<FullLeaf> FullLeafPtr;
-typedef boost::shared_ptr<const FullLeaf> FullLeafConstPtr;
-
-} // Sync
-
-#endif // SYNC_FULL_LEAF_H
diff --git a/src/sync-full-state.cc b/src/sync-full-state.cc
deleted file mode 100644
index 651f6ab..0000000
--- a/src/sync-full-state.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-full-state.h"
-
-#include <boost/make_shared.hpp>
-#include <boost/lambda/lambda.hpp>
-#include <boost/lambda/bind.hpp>
-#include <boost/foreach.hpp>
-#include <boost/assert.hpp>
-
-#include "sync-full-leaf.h"
-
-using namespace boost;
-namespace ll = boost::lambda;
-
-namespace Sync {
-
-
-FullState::FullState ()
-// m_lastUpdated is initialized to "not_a_date_time" in normal lib mode and to "0" time in NS-3 mode
-{
-}
-
-FullState::~FullState ()
-{
-}
-
-ndn::time::system_clock::Duration
-FullState::getTimeFromLastUpdate () const
-{
- return ndn::time::system_clock::now() - m_lastUpdated;
-}
-
-DigestConstPtr
-FullState::getDigest ()
-{
- if (!m_digest)
- {
- m_digest = make_shared<Digest> ();
- if (m_leaves.get<ordered> ().size () > 0)
- {
- BOOST_FOREACH (LeafConstPtr leaf, m_leaves.get<ordered> ())
- {
- FullLeafConstPtr fullLeaf = dynamic_pointer_cast<const FullLeaf> (leaf);
- BOOST_ASSERT (fullLeaf != 0);
- *m_digest << fullLeaf->getDigest ();
- }
- m_digest->finalize ();
- }
- else
- {
- std::istringstream is ("00"); //zero state
- is >> *m_digest;
- }
- }
-
- return m_digest;
-}
-
-// from State
-boost::tuple<bool/*inserted*/, bool/*updated*/, SeqNo/*oldSeqNo*/>
-FullState::update (NameInfoConstPtr info, const SeqNo &seq)
-{
- m_lastUpdated = ndn::time::system_clock::now();
-
-
- m_digest.reset ();
-
- LeafContainer::iterator item = m_leaves.find (info);
- if (item == m_leaves.end ())
- {
- m_leaves.insert (make_shared<FullLeaf> (info, cref (seq)));
- return make_tuple (true, false, SeqNo ());
- }
- else
- {
- if ((*item)->getSeq () == seq || seq < (*item)->getSeq ())
- {
- return make_tuple (false, false, SeqNo ());
- }
-
- SeqNo old = (*item)->getSeq ();
- m_leaves.modify (item,
- ll::bind (&Leaf::setSeq, *ll::_1, seq));
- return make_tuple (false, true, old);
- }
-}
-
-bool
-FullState::remove (NameInfoConstPtr info)
-{
- m_lastUpdated = ndn::time::system_clock::now();
-
- m_digest.reset ();
-
- LeafContainer::iterator item = m_leaves.find (info);
- if (item != m_leaves.end ())
- {
- m_leaves.erase (item);
- return true;
- }
- else
- return false;
-}
-
-} // Sync
diff --git a/src/sync-full-state.h b/src/sync-full-state.h
deleted file mode 100644
index 77533b2..0000000
--- a/src/sync-full-state.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_FULL_STATE_H
-#define SYNC_FULL_STATE_H
-
-#include <ndn-cxx/util/time.hpp>
-#include "sync-state.h"
-
-namespace Sync {
-
-class FullState;
-typedef boost::shared_ptr<FullState> FullStatePtr;
-typedef boost::shared_ptr<FullState> FullStateConstPtr;
-
-
-/**
- * \ingroup sync
- * @brief Cumulative SYNC state
- */
-class FullState : public State
-{
-public:
- /**
- * @brief Default constructor
- */
- FullState ();
- virtual ~FullState ();
-
- /**
- * @brief Get time period since last state update
- *
- * This value can be used to randomize reconciliation waiting time in SyncApp
- */
- ndn::time::system_clock::Duration
- getTimeFromLastUpdate () const;
-
- /**
- * @brief Obtain a read-only copy of the digest
- *
- * If m_digest is 0, then it is automatically created. On every update and removal, m_digest is reset to 0
- */
- DigestConstPtr
- getDigest ();
-
- // from State
- virtual boost::tuple<bool/*inserted*/, bool/*updated*/, SeqNo/*oldSeqNo*/>
- update (NameInfoConstPtr info, const SeqNo &seq);
-
- virtual bool
- remove (NameInfoConstPtr info);
-
-private:
- ndn::time::system_clock::TimePoint m_lastUpdated; ///< @brief Time when state was updated last time
- DigestPtr m_digest;
-};
-
-} // Sync
-
-#endif // SYNC_STATE_H
diff --git a/src/sync-interest-container.h b/src/sync-interest-container.h
deleted file mode 100644
index 86b7460..0000000
--- a/src/sync-interest-container.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_INTEREST_CONTAINER_H
-#define SYNC_INTEREST_CONTAINER_H
-
-#include <ndn-cxx/util/time.hpp>
-
-#include "sync-digest.h"
-
-#include <boost/multi_index_container.hpp>
-#include <boost/multi_index/tag.hpp>
-// #include <boost/multi_index/ordered_index.hpp>
-// #include <boost/multi_index/composite_key.hpp>
-#include <boost/multi_index/hashed_index.hpp>
-#include <boost/multi_index/sequenced_index.hpp>
-#include <boost/multi_index/ordered_index.hpp>
-// #include <boost/multi_index/random_access_index.hpp>
-#include <boost/multi_index/member.hpp>
-#include <boost/multi_index/mem_fun.hpp>
-
-namespace mi = boost::multi_index;
-
-namespace Sync {
-
-struct Interest
-{
- Interest (DigestConstPtr digest, const std::string &name, bool unknown=false)
- : m_digest (digest)
- , m_name (name)
- , m_time (ndn::time::system_clock::now())
- , m_unknown (unknown)
- {
- }
-
- DigestConstPtr m_digest;
- std::string m_name;
- ndn::time::system_clock::TimePoint m_time;
- bool m_unknown;
-};
-
-/// @cond include_hidden
-struct named { };
-struct hashed;
-struct timed;
-/// @endcond
-
-/**
- * \ingroup sync
- * @brief Container for interests (application PIT)
- */
-struct InterestContainer : public mi::multi_index_container<
- Interest,
- mi::indexed_by<
- mi::hashed_unique<
- mi::tag<named>,
- BOOST_MULTI_INDEX_MEMBER(Interest, std::string, m_name)
- >
- ,
-
- mi::hashed_non_unique<
- mi::tag<hashed>,
- BOOST_MULTI_INDEX_MEMBER(Interest, DigestConstPtr, m_digest),
- DigestPtrHash,
- DigestPtrEqual
- >
- ,
-
- mi::ordered_non_unique<
- mi::tag<timed>,
- BOOST_MULTI_INDEX_MEMBER(Interest, ndn::time::system_clock::TimePoint, m_time)
- >
- >
- >
-{
-};
-
-} // Sync
-
-#endif // SYNC_INTEREST_CONTAINER_H
diff --git a/src/sync-interest-table.cc b/src/sync-interest-table.cc
deleted file mode 100644
index 548ebe2..0000000
--- a/src/sync-interest-table.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-interest-table.h"
-#include "sync-logging.h"
-
-INIT_LOGGER ("SyncInterestTable")
-
-namespace Sync
-{
-
-SyncInterestTable::SyncInterestTable (boost::asio::io_service& io, ndn::time::system_clock::Duration lifetime)
- : m_entryLifetime (lifetime)
- , m_scheduler(io)
-{
- m_scheduler.scheduleEvent(ndn::time::seconds (4),
- ndn::bind(&SyncInterestTable::expireInterests, this));
-}
-
-SyncInterestTable::~SyncInterestTable ()
-{
-}
-
-Interest
-SyncInterestTable::pop ()
-{
- if (m_table.size () == 0)
- BOOST_THROW_EXCEPTION (Error::InterestTableIsEmpty ());
-
- Interest ret = *m_table.begin ();
- m_table.erase (m_table.begin ());
-
- return ret;
-}
-
-bool
-SyncInterestTable::insert (DigestConstPtr digest, const std::string& name, bool unknownState/*=false*/)
-{
- bool existent = false;
-
- InterestContainer::index<named>::type::iterator it = m_table.get<named> ().find (name);
- if (it != m_table.end())
- {
- existent = true;
- m_table.erase (it);
- }
- m_table.insert (Interest (digest, name, unknownState));
-
- return existent;
-}
-
-uint32_t
-SyncInterestTable::size () const
-{
- return m_table.size ();
-}
-
-bool
-SyncInterestTable::remove (const std::string& name)
-{
- InterestContainer::index<named>::type::iterator item = m_table.get<named> ().find (name);
- if (item != m_table.get<named> ().end ())
- {
- m_table.get<named> ().erase (name);
- return true;
- }
-
- return false;
-}
-
-bool
-SyncInterestTable::remove (DigestConstPtr digest)
-{
- InterestContainer::index<hashed>::type::iterator item = m_table.get<hashed> ().find (digest);
- if (item != m_table.get<hashed> ().end ())
- {
- m_table.get<hashed> ().erase (digest); // erase all records associated with the digest
- return true;
- }
- return false;
-}
-
-void SyncInterestTable::expireInterests ()
-{
- uint32_t count = 0;
- ndn::time::system_clock::TimePoint expireTime = ndn::time::system_clock::now() - m_entryLifetime;
-
- while (m_table.size () > 0)
- {
- InterestContainer::index<timed>::type::iterator item = m_table.get<timed> ().begin ();
-
- if (item->m_time <= expireTime)
- {
- m_table.get<timed> ().erase (item);
- count ++;
- }
- else
- break;
- }
-
- m_scheduler.scheduleEvent(ndn::time::seconds (4),
- ndn::bind(&SyncInterestTable::expireInterests, this));
-}
-
-
-}
diff --git a/src/sync-interest-table.h b/src/sync-interest-table.h
deleted file mode 100644
index fad339f..0000000
--- a/src/sync-interest-table.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_INTEREST_TABLE_H
-#define SYNC_INTEREST_TABLE_H
-
-#include <ndn-cxx/util/scheduler.hpp>
-
-#include <string>
-#include <vector>
-
-#include "sync-digest.h"
-#include "sync-interest-container.h"
-
-namespace Sync {
-
-/**
- * \ingroup sync
- * @brief A table to keep unanswered Sync Interest
- * all access operation to the table should grab the
- * mutex first
- */
-class SyncInterestTable
-{
-public:
- SyncInterestTable (boost::asio::io_service& io, ndn::time::system_clock::Duration lifetime);
- ~SyncInterestTable ();
-
- /**
- * @brief Insert an interest, if interest already exists, update the
- * timestamp
- */
- bool
- insert (DigestConstPtr interest, const std::string &name, bool unknownState=false);
-
- /**
- * @brief Remove interest by digest (e.g., when it was satisfied)
- */
- bool
- remove (DigestConstPtr interest);
-
- /**
- * @brief Remove interest by name (e.g., when it was satisfied)
- */
- bool
- remove (const std::string &name);
-
- /**
- * @brief pop a non-expired Interest from PIT
- */
- Interest
- pop ();
-
- uint32_t
- size () const;
-
-private:
- /**
- * @brief periodically called to expire Interest
- */
- void
- expireInterests ();
-
-private:
- ndn::time::system_clock::Duration m_entryLifetime;
- InterestContainer m_table;
-
- ndn::Scheduler m_scheduler;
-};
-
-namespace Error {
-struct InterestTableIsEmpty : virtual boost::exception, virtual std::exception { };
-}
-
-} // Sync
-
-#endif // SYNC_INTEREST_TABLE_H
diff --git a/src/sync-leaf.cc b/src/sync-leaf.cc
deleted file mode 100644
index e117ca3..0000000
--- a/src/sync-leaf.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-leaf.h"
-
-namespace Sync {
-
-Leaf::Leaf (NameInfoConstPtr info, const SeqNo &seq)
- : m_info (info)
- , m_seq (seq)
-{
-}
-
-Leaf::~Leaf ()
-{
-}
-
-void
-Leaf::setSeq (const SeqNo &seq)
-{
- m_seq = std::max (m_seq, seq);
-}
-
-} // Sync
diff --git a/src/sync-leaf.h b/src/sync-leaf.h
deleted file mode 100644
index 567ab7e..0000000
--- a/src/sync-leaf.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_LEAF_H
-#define SYNC_LEAF_H
-
-#include "sync-seq-no.h"
-#include "sync-name-info.h"
-
-namespace Sync {
-
-/**
- * \ingroup sync
- * @brief Sync tree leaf
- */
-class Leaf
-{
-public:
- /**
- * @brief Constructor
- * @param info Smart pointer to leaf's name
- * @param seq Initial sequence number of the pointer
- */
- Leaf (NameInfoConstPtr info, const SeqNo &seq);
- virtual ~Leaf ();
-
- /**
- * @brief Get name of the leaf
- */
- NameInfoConstPtr
- getInfo () const { return m_info; }
-
- /**
- * @brief Get sequence number of the leaf
- */
- const SeqNo&
- getSeq () const { return m_seq; }
-
- /**
- * @brief Update sequence number of the leaf
- * @param seq Sequence number
- *
- * Sequence number is updated to the largest value among this->m_seq and seq
- */
- virtual void
- setSeq (const SeqNo &seq);
-
-private:
- NameInfoConstPtr m_info;
- SeqNo m_seq;
-};
-
-typedef boost::shared_ptr<Leaf> LeafPtr;
-typedef boost::shared_ptr<const Leaf> LeafConstPtr;
-
-inline std::ostream &
-operator << (std::ostream &os, const Leaf &leaf)
-{
- os << *leaf.getInfo () << "(" << leaf.getSeq () << ")";
- return os;
-}
-
-} // Sync
-
-#endif // SYNC_LEAF_H
diff --git a/src/sync-logging.cc b/src/sync-logging.cc
deleted file mode 100644
index 9780a5a..0000000
--- a/src/sync-logging.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-logging.h"
-
-#ifdef HAVE_LOG4CXX
-
-#include <log4cxx/logger.h>
-#include <log4cxx/basicconfigurator.h>
-#include <log4cxx/consoleappender.h>
-#include <log4cxx/patternlayout.h>
-#include <log4cxx/level.h>
-#include <log4cxx/propertyconfigurator.h>
-#include <log4cxx/defaultconfigurator.h>
-#include <log4cxx/helpers/exception.h>
-using namespace log4cxx;
-using namespace log4cxx::helpers;
-
-#include <unistd.h>
-
-void
-INIT_LOGGERS ()
-{
- static bool configured = false;
-
- if (configured) return;
-
- if (access ("log4cxx.properties", R_OK)==0)
- PropertyConfigurator::configureAndWatch ("log4cxx.properties");
- else
- {
- PatternLayoutPtr layout (new PatternLayout ("%d{HH:mm:ss} %p %c{1} - %m%n"));
- ConsoleAppenderPtr appender (new ConsoleAppender (layout));
-
- BasicConfigurator::configure( appender );
- Logger::getRootLogger()->setLevel (log4cxx::Level::getInfo ());
- }
-
- configured = true;
-}
-
-#endif
diff --git a/src/sync-logic-event-container.h b/src/sync-logic-event-container.h
deleted file mode 100644
index 015984d..0000000
--- a/src/sync-logic-event-container.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_LOGIC_EVENT_CONTAINER_H
-#define SYNC_LOGIC_EVENT_CONTAINER_H
-
-#include "sync-event.h"
-
-#include <boost/function.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-
-#include <boost/multi_index_container.hpp>
-// #include <boost/multi_index/tag.hpp>
-#include <boost/multi_index/ordered_index.hpp>
-// #include <boost/multi_index/composite_key.hpp>
-// #include <boost/multi_index/hashed_index.hpp>
-// #include <boost/multi_index/random_access_index.hpp>
-#include <boost/multi_index/member.hpp>
-// #include <boost/multi_index/mem_fun.hpp>
-
-namespace mi = boost::multi_index;
-
-namespace Sync
-{
-
-struct LogicEvent
-{
- LogicEvent (const boost::system_time &_time, Event _event, uint32_t _label)
- : time (_time)
- , event (_event)
- , lbl (_label)
- { }
-
- boost::system_time time;
- Event event;
- uint32_t lbl;
-};
-
-/// @cond include_hidden
-struct byLabel { } ;
-/// @endcond
-
-/**
- * \ingroup sync
- * @brief ???
- */
-struct EventsContainer : public mi::multi_index_container<
- LogicEvent,
- mi::indexed_by<
-
- mi::ordered_non_unique<
- mi::member<LogicEvent, boost::system_time, &LogicEvent::time>
- >,
-
- mi::ordered_non_unique<
- mi::tag<byLabel>,
- mi::member<LogicEvent, uint32_t, &LogicEvent::lbl>
- >
- >
- >
-{
-};
-
-} // Sync
-
-#endif // SYNC_LOGIC_EVENT_CONTAINER_H
diff --git a/src/sync-logic.cc b/src/sync-logic.cc
deleted file mode 100644
index 0567b60..0000000
--- a/src/sync-logic.cc
+++ /dev/null
@@ -1,714 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/web/index.html>
- */
-
-#include "sync-logic.h"
-#include "sync-diff-leaf.h"
-#include "sync-full-leaf.h"
-#include "sync-logging.h"
-#include "sync-state.h"
-
-#include <boost/foreach.hpp>
-#include <boost/lexical_cast.hpp>
-#include <vector>
-
-using namespace ndn;
-
-INIT_LOGGER ("SyncLogic")
-
-
-#ifdef _DEBUG
-#define _LOG_DEBUG_ID(v) _LOG_DEBUG(m_instanceId << " " << v)
-#else
-#define _LOG_DEBUG_ID(v) _LOG_DEBUG(v)
-#endif
-
-#define GET_RANDOM(var) var ()
-
-#define TIME_SECONDS_WITH_JITTER(sec) \
- (time::seconds(sec) + time::milliseconds(GET_RANDOM (m_reexpressionJitter)))
-
-#define TIME_MILLISECONDS_WITH_JITTER(ms) \
- (time::milliseconds(ms) + time::milliseconds(GET_RANDOM (m_reexpressionJitter)))
-
-namespace Sync {
-
-using ndn::shared_ptr;
-
-int SyncLogic::m_instanceCounter = 0;
-
-const int SyncLogic::m_syncResponseFreshness = 1000; // MUST BE dividable by 1000!!!
-const int SyncLogic::m_syncInterestReexpress = 4; // seconds
-
-SyncLogic::SyncLogic (const Name& syncPrefix,
- const IdentityCertificate& myCertificate,
- shared_ptr<Validator> validator,
- shared_ptr<Face> face,
- LogicUpdateCallback onUpdate,
- LogicRemoveCallback onRemove)
- : m_state (new FullState)
- , m_syncInterestTable (face->getIoService(), time::seconds(m_syncInterestReexpress))
- , m_syncPrefix (syncPrefix)
- , m_myCertificate(myCertificate)
- , m_onUpdate (onUpdate)
- , m_onRemove (onRemove)
- , m_perBranch (false)
- , m_validator(validator)
- , m_face(face)
- , m_scheduler(face->getIoService())
- , m_randomGenerator (static_cast<unsigned int> (std::time (0)))
- , m_rangeUniformRandom (m_randomGenerator, boost::uniform_int<> (200,1000))
- , m_reexpressionJitter (m_randomGenerator, boost::uniform_int<> (100,500))
- , m_recoveryRetransmissionInterval (m_defaultRecoveryRetransmitInterval)
-{
- m_syncRegisteredPrefixId = m_face->setInterestFilter (m_syncPrefix,
- bind(&SyncLogic::onSyncInterest, this, _1, _2),
- bind(&SyncLogic::onSyncRegisterFailed, this, _1, _2));
-
-
- m_reexpressingInterestId = m_scheduler.scheduleEvent (time::seconds (0), // no need to add jitter
- bind (&SyncLogic::sendSyncInterest, this));
-
- m_instanceId = std::string("Instance " + boost::lexical_cast<std::string>(m_instanceCounter++) + " ");
-}
-
-SyncLogic::SyncLogic (const Name& syncPrefix,
- const IdentityCertificate& myCertificate,
- shared_ptr<Validator> validator,
- shared_ptr<Face> face,
- LogicPerBranchCallback onUpdateBranch)
- : m_state (new FullState)
- , m_syncInterestTable (face->getIoService(), time::seconds (m_syncInterestReexpress))
- , m_syncPrefix (syncPrefix)
- , m_myCertificate(myCertificate)
- , m_onUpdateBranch (onUpdateBranch)
- , m_perBranch(true)
- , m_validator(validator)
- , m_face(face)
- , m_scheduler(face->getIoService())
- , m_randomGenerator (static_cast<unsigned int> (std::time (0)))
- , m_rangeUniformRandom (m_randomGenerator, boost::uniform_int<> (200,1000))
- , m_reexpressionJitter (m_randomGenerator, boost::uniform_int<> (100,500))
- , m_recoveryRetransmissionInterval (m_defaultRecoveryRetransmitInterval)
-{
- m_syncRegisteredPrefixId = m_face->setInterestFilter (m_syncPrefix,
- bind(&SyncLogic::onSyncInterest, this, _1, _2),
- bind(&SyncLogic::onSyncRegisterFailed, this, _1, _2));
-
- m_reexpressingInterestId = m_scheduler.scheduleEvent (time::seconds (0), // no need to add jitter
- bind (&SyncLogic::sendSyncInterest, this));
-}
-
-SyncLogic::~SyncLogic ()
-{
- m_face->unsetInterestFilter(m_syncRegisteredPrefixId);
- m_scheduler.cancelEvent (m_reexpressingInterestId);
- m_scheduler.cancelEvent (m_delayedInterestProcessingId);
-}
-
-/**
- * Two types of intersts
- *
- * Normal name: .../<hash>
- * Recovery name: .../recovery/<hash>
- */
-boost::tuple<DigestConstPtr, std::string>
-SyncLogic::convertNameToDigestAndType (const Name &name)
-{
- BOOST_ASSERT (m_syncPrefix.isPrefixOf(name));
-
- int nameLengthDiff = name.size() - m_syncPrefix.size();
- BOOST_ASSERT (nameLengthDiff > 0);
- BOOST_ASSERT (nameLengthDiff < 3);
-
- std::string hash = name.get(-1).toUri();
- std::string interestType;
-
- if(nameLengthDiff == 1)
- interestType = "normal";
- else
- interestType = name.get(-2).toUri();
-
- _LOG_DEBUG_ID (hash << ", " << interestType);
-
- DigestPtr digest = boost::make_shared<Digest> ();
- std::istringstream is (hash);
- is >> *digest;
-
- return make_tuple (digest, interestType);
-}
-
-void
-SyncLogic::onSyncInterest (const Name& prefix, const ndn::Interest& interest)
-{
- Name name = interest.getName();
-
- _LOG_DEBUG_ID("respondSyncInterest: " << name);
-
- try
- {
- _LOG_DEBUG_ID ("<< I " << name);
-
- if(name.get(m_syncPrefix.size()).toUri() == "CHRONOS-INTRO-CERT")
- // it is a certificate, validator will take care of it.
- return;
-
- DigestConstPtr digest;
- std::string type;
- tie (digest, type) = convertNameToDigestAndType (name);
-
- if (type == "normal") // kind of ineffective...
- {
- processSyncInterest (name, digest);
- }
- else if (type == "recovery")
- {
- processSyncRecoveryInterest (name, digest);
- }
- }
- catch (Error::DigestCalculationError &e)
- {
- _LOG_DEBUG_ID ("Something fishy happened...");
- // log error. ignoring it for now, later we should log it
- return ;
- }
-}
-
-void
-SyncLogic::onSyncRegisterFailed(const Name& prefix, const std::string& msg)
-{
- _LOG_DEBUG_ID("Sync prefix registration failed! " << msg);
-}
-
-void
-SyncLogic::onSyncData(const ndn::Interest& interest, Data& data)
-{
- OnDataValidated onValidated = bind(&SyncLogic::onSyncDataValidated, this, _1);
- OnDataValidationFailed onValidationFailed = bind(&SyncLogic::onSyncDataValidationFailed, this, _1);
- m_validator->validate(data, onValidated, onValidationFailed);
-}
-
-void
-SyncLogic::onSyncTimeout(const ndn::Interest& interest)
-{
- // It is OK. Others will handle the time out situation.
-}
-
-void
-SyncLogic::onSyncDataValidationFailed(const shared_ptr<const Data>& data)
-{
- _LOG_DEBUG_ID("Sync data cannot be verified!");
-}
-
-void
-SyncLogic::onSyncDataValidated(const shared_ptr<const Data>& data)
-{
- Name name = data->getName();
- const char* wireData = (const char*)data->getContent().value();
- size_t len = data->getContent().value_size();
-
- try
- {
- _LOG_DEBUG_ID ("<< D " << name);
-
- DigestConstPtr digest;
- std::string type;
- tie (digest, type) = convertNameToDigestAndType (name);
-
- if (type == "normal")
- {
- processSyncData (name, digest, wireData, len);
- }
- else
- {
- // timer is always restarted when we schedule recovery
- m_scheduler.cancelEvent (m_reexpressingRecoveryInterestId);
- processSyncData (name, digest, wireData, len);
- }
- }
- catch (Error::DigestCalculationError &e)
- {
- _LOG_DEBUG_ID ("Something fishy happened...");
- // log error. ignoring it for now, later we should log it
- return;
- }
-}
-
-void
-SyncLogic::processSyncInterest (const Name &name, DigestConstPtr digest, bool timedProcessing/*=false*/)
-{
- _LOG_DEBUG_ID("processSyncInterest");
- DigestConstPtr rootDigest;
- {
- rootDigest = m_state->getDigest();
- }
-
- // Special case when state is not empty and we have received request with zero-root digest
- if (digest->isZero () && !rootDigest->isZero ())
- {
-
- SyncStateMsg ssm;
- {
- ssm << (*m_state);
- }
- sendSyncData (name, digest, ssm);
- return;
- }
-
- if (*rootDigest == *digest)
- {
- _LOG_DEBUG_ID ("processSyncInterest (): Same state. Adding to PIT");
- m_syncInterestTable.insert (digest, name.toUri(), false);
- return;
- }
-
- DiffStateContainer::iterator stateInDiffLog = m_log.find (digest);
-
- if (stateInDiffLog != m_log.end ())
- {
- DiffStateConstPtr stateDiff = (*stateInDiffLog)->diff ();
-
- sendSyncData (name, digest, stateDiff);
- return;
- }
-
- if (!timedProcessing)
- {
- bool exists = m_syncInterestTable.insert (digest, name.toUri(), true);
- if (exists) // somebody else replied, so restart random-game timer
- {
- _LOG_DEBUG_ID ("Unknown digest, but somebody may have already replied, so restart our timer");
- m_scheduler.cancelEvent (m_delayedInterestProcessingId);
- }
-
- uint32_t waitDelay = GET_RANDOM (m_rangeUniformRandom);
- _LOG_DEBUG_ID ("Digest is not in the log. Schedule processing after small delay: " << time::milliseconds (waitDelay));
-
- m_delayedInterestProcessingId = m_scheduler.scheduleEvent (time::milliseconds (waitDelay),
- bind (&SyncLogic::processSyncInterest, this, name, digest, true));
- }
- else
- {
- _LOG_DEBUG_ID (" (timed processing)");
-
- m_recoveryRetransmissionInterval = m_defaultRecoveryRetransmitInterval;
- sendSyncRecoveryInterests (digest);
- }
-}
-
-void
-SyncLogic::processSyncData (const Name &name, DigestConstPtr digest, const char *wireData, size_t len)
-{
- DiffStatePtr diffLog = boost::make_shared<DiffState> ();
- bool ownInterestSatisfied = false;
-
- try
- {
-
- m_syncInterestTable.remove (name.toUri()); // Remove satisfied interest from PIT
-
- ownInterestSatisfied = (name == m_outstandingInterestName);
-
- DiffState diff;
- SyncStateMsg msg;
- if (!msg.ParseFromArray(wireData, len) || !msg.IsInitialized())
- {
- //Throw
- BOOST_THROW_EXCEPTION (Error::SyncStateMsgDecodingFailure () );
- }
- msg >> diff;
-
- std::vector<MissingDataInfo> v;
- BOOST_FOREACH (LeafConstPtr leaf, diff.getLeaves().get<ordered>())
- {
- DiffLeafConstPtr diffLeaf = boost::dynamic_pointer_cast<const DiffLeaf> (leaf);
- BOOST_ASSERT (diffLeaf != 0);
-
- NameInfoConstPtr info = diffLeaf->getInfo();
- if (diffLeaf->getOperation() == UPDATE)
- {
- SeqNo seq = diffLeaf->getSeq();
-
- bool inserted = false;
- bool updated = false;
- SeqNo oldSeq;
- {
- boost::tie (inserted, updated, oldSeq) = m_state->update (info, seq);
- }
-
- if (inserted || updated)
- {
- diffLog->update (info, seq);
- if (!oldSeq.isValid())
- {
- oldSeq = SeqNo(seq.getSession(), 0);
- }
- else
- {
- ++oldSeq;
- }
- // there is no need for application to process update on forwarder node
- if (info->toString() != forwarderPrefix)
- {
- MissingDataInfo mdi = {info->toString(), oldSeq, seq};
- {
- std::ostringstream interestName;
- interestName << mdi.prefix << "/" << mdi.high.getSession() << "/" << mdi.high.getSeq();
- _LOG_DEBUG_ID("+++++++++++++++ " + interestName.str());
- }
- if (m_perBranch)
- {
- std::ostringstream interestName;
- interestName << mdi.prefix << "/" << mdi.high.getSession() << "/" << mdi.high.getSeq();
- m_onUpdateBranch(interestName.str());
- }
- else
- {
- v.push_back(mdi);
- }
- }
- }
- }
- else if (diffLeaf->getOperation() == REMOVE)
- {
- if (m_state->remove (info))
- {
- diffLog->remove (info);
- if (!m_perBranch)
- {
- m_onRemove (info->toString ());
- }
- }
- }
- else
- {
- }
- }
-
- if (!v.empty())
- {
- if (!m_perBranch)
- {
- m_onUpdate(v);
- }
- }
-
- insertToDiffLog (diffLog);
- }
- catch (Error::SyncStateMsgDecodingFailure &e)
- {
- _LOG_DEBUG_ID ("Something really fishy happened during state decoding " <<
- diagnostic_information (e));
- diffLog.reset ();
- // don't do anything
- }
-
- if ((diffLog != 0 && diffLog->getLeaves ().size () > 0) ||
- ownInterestSatisfied)
- {
- _LOG_DEBUG_ID(" +++++++++++++++ state changed!!!");
- // Do it only if everything went fine and state changed
-
- // this is kind of wrong
- // satisfyPendingSyncInterests (diffLog); // if there are interests in PIT, there is a point to satisfy them using new state
-
- // if state has changed, then it is safe to express a new interest
- time::system_clock::Duration after = time::milliseconds(GET_RANDOM (m_reexpressionJitter));
- // cout << "------------ reexpress interest after: " << after << endl;
- EventId eventId = m_scheduler.scheduleEvent (after,
- bind (&SyncLogic::sendSyncInterest, this));
-
- m_scheduler.cancelEvent (m_reexpressingInterestId);
- m_reexpressingInterestId = eventId;
- }
-}
-
-void
-SyncLogic::processSyncRecoveryInterest (const Name &name, DigestConstPtr digest)
-{
- _LOG_DEBUG_ID("processSyncRecoveryInterest");
- DiffStateContainer::iterator stateInDiffLog = m_log.find (digest);
-
- if (stateInDiffLog == m_log.end ())
- {
- _LOG_DEBUG_ID ("Could not find " << *digest << " in digest log");
- return;
- }
-
- SyncStateMsg ssm;
- {
- ssm << (*m_state);
- }
- sendSyncData (name, digest, ssm);
-}
-
-void
-SyncLogic::satisfyPendingSyncInterests (DiffStateConstPtr diffLog)
-{
- DiffStatePtr fullStateLog = boost::make_shared<DiffState> ();
- {
- BOOST_FOREACH (LeafConstPtr leaf, m_state->getLeaves ()/*.get<timed> ()*/)
- {
- fullStateLog->update (leaf->getInfo (), leaf->getSeq ());
- /// @todo Impose limit on how many state info should be send out
- }
- }
-
- try
- {
- uint32_t counter = 0;
- while (m_syncInterestTable.size () > 0)
- {
- Sync::Interest interest = m_syncInterestTable.pop ();
-
- if (!interest.m_unknown)
- {
- _LOG_DEBUG_ID (">> D " << interest.m_name);
- sendSyncData (interest.m_name, interest.m_digest, diffLog);
- }
- else
- {
- _LOG_DEBUG_ID (">> D (unknown)" << interest.m_name);
- sendSyncData (interest.m_name, interest.m_digest, fullStateLog);
- }
- counter ++;
- }
- _LOG_DEBUG_ID ("Satisfied " << counter << " pending interests");
- }
- catch (Error::InterestTableIsEmpty &e)
- {
- // ok. not really an error
- }
-}
-
-void
-SyncLogic::insertToDiffLog (DiffStatePtr diffLog)
-{
- diffLog->setDigest (m_state->getDigest());
- if (m_log.size () > 0)
- {
- m_log.get<sequenced> ().front ()->setNext (diffLog);
- }
- m_log.erase (m_state->getDigest()); // remove diff state with the same digest. next pointers are still valid
- /// @todo Optimization
- m_log.get<sequenced> ().push_front (diffLog);
-}
-
-void
-SyncLogic::addLocalNames (const Name &prefix, uint64_t session, uint64_t seq)
-{
- DiffStatePtr diff;
- {
- //cout << "Add local names" <<endl;
- NameInfoConstPtr info = StdNameInfo::FindOrCreate(prefix.toUri());
-
- _LOG_DEBUG_ID ("addLocalNames (): old state " << *m_state->getDigest ());
-
- SeqNo seqN (session, seq);
- m_state->update(info, seqN);
-
- _LOG_DEBUG_ID ("addLocalNames (): new state " << *m_state->getDigest ());
-
- diff = boost::make_shared<DiffState>();
- diff->update(info, seqN);
- insertToDiffLog (diff);
- }
-
- // _LOG_DEBUG_ID ("PIT size: " << m_syncInterestTable.size ());
- satisfyPendingSyncInterests (diff);
-}
-
-void
-SyncLogic::remove(const Name &prefix)
-{
- DiffStatePtr diff;
- {
- NameInfoConstPtr info = StdNameInfo::FindOrCreate(prefix.toUri());
- m_state->remove(info);
-
- // increment the sequence number for the forwarder node
- NameInfoConstPtr forwarderInfo = StdNameInfo::FindOrCreate(forwarderPrefix);
-
- LeafContainer::iterator item = m_state->getLeaves ().find (forwarderInfo);
- SeqNo seqNo (0);
- if (item != m_state->getLeaves ().end ())
- {
- seqNo = (*item)->getSeq ();
- ++seqNo;
- }
- m_state->update (forwarderInfo, seqNo);
-
- diff = boost::make_shared<DiffState>();
- diff->remove(info);
- diff->update(forwarderInfo, seqNo);
-
- insertToDiffLog (diff);
- }
-
- satisfyPendingSyncInterests (diff);
-}
-
-void
-SyncLogic::sendSyncInterest ()
-{
- _LOG_DEBUG_ID("sendSyncInterest");
-
- {
- m_outstandingInterestName = m_syncPrefix;
- std::ostringstream os;
- os << *m_state->getDigest();
- m_outstandingInterestName.append(os.str());
- _LOG_DEBUG_ID (">> I " << m_outstandingInterestName);
- }
-
- _LOG_DEBUG_ID("sendSyncInterest: " << m_outstandingInterestName);
-
- EventId eventId = m_scheduler.scheduleEvent (time::seconds(m_syncInterestReexpress) + time::milliseconds(GET_RANDOM (m_reexpressionJitter)),
- bind (&SyncLogic::sendSyncInterest, this));
- m_scheduler.cancelEvent (m_reexpressingInterestId);
- m_reexpressingInterestId = eventId;
-
- ndn::Interest interest(m_outstandingInterestName);
- interest.setMustBeFresh(true);
-
- m_face->expressInterest(interest,
- bind(&SyncLogic::onSyncData, this, _1, _2),
- bind(&SyncLogic::onSyncTimeout, this, _1));
-}
-
-void
-SyncLogic::sendSyncRecoveryInterests (DigestConstPtr digest)
-{
- std::ostringstream os;
- os << *digest;
-
- Name interestName = m_syncPrefix;
- interestName.append("recovery").append(os.str());
-
- time::system_clock::Duration nextRetransmission = time::milliseconds (m_recoveryRetransmissionInterval + GET_RANDOM (m_reexpressionJitter));
-
- m_recoveryRetransmissionInterval <<= 1;
-
- m_scheduler.cancelEvent (m_reexpressingRecoveryInterestId);
- if (m_recoveryRetransmissionInterval < 100*1000) // <100 seconds
- m_reexpressingRecoveryInterestId = m_scheduler.scheduleEvent (nextRetransmission,
- bind (&SyncLogic::sendSyncRecoveryInterests, this, digest));
-
- ndn::Interest interest(interestName);
- interest.setMustBeFresh(true);
-
- m_face->expressInterest(interest,
- bind(&SyncLogic::onSyncData, this, _1, _2),
- bind(&SyncLogic::onSyncTimeout, this, _1));
-}
-
-
-void
-SyncLogic::sendSyncData (const Name &name, DigestConstPtr digest, StateConstPtr state)
-{
- SyncStateMsg msg;
- msg << (*state);
- sendSyncData(name, digest, msg);
-}
-
-// pass in state msg instead of state, so that there is no need to lock the state until
-// this function returns
-void
-SyncLogic::sendSyncData (const Name &name, DigestConstPtr digest, SyncStateMsg &ssm)
-{
- _LOG_DEBUG_ID (">> D " << name);
- int size = ssm.ByteSize();
- char *wireData = new char[size];
- ssm.SerializeToArray(wireData, size);
-
- Data syncData(name);
- syncData.setContent(reinterpret_cast<const uint8_t*>(wireData), size);
- syncData.setFreshnessPeriod(time::milliseconds(m_syncResponseFreshness));
-
- m_keyChain.sign(syncData, m_myCertificate.getName());
-
- m_face->put(syncData);
-
- delete []wireData;
-
- // checking if our own interest got satisfied
- bool satisfiedOwnInterest = false;
- {
- satisfiedOwnInterest = (m_outstandingInterestName == name);
- }
-
- if (satisfiedOwnInterest)
- {
- _LOG_DEBUG_ID ("Satisfied our own Interest. Re-expressing (hopefully with a new digest)");
-
- time::system_clock::Duration after = time::milliseconds(GET_RANDOM (m_reexpressionJitter));
- // cout << "------------ reexpress interest after: " << after << endl;
- EventId eventId = m_scheduler.scheduleEvent (after,
- bind (&SyncLogic::sendSyncInterest, this));
- m_scheduler.cancelEvent (m_reexpressingInterestId);
- m_reexpressingInterestId = eventId;
- }
-}
-
-std::string
-SyncLogic::getRootDigest()
-{
- std::ostringstream os;
- os << *m_state->getDigest();
- return os.str();
-}
-
-size_t
-SyncLogic::getNumberOfBranches () const
-{
- return m_state->getLeaves ().size ();
-}
-
-void
-SyncLogic::printState () const
-{
- BOOST_FOREACH (const boost::shared_ptr<Sync::Leaf> leaf, m_state->getLeaves ())
- {
- std::cout << *leaf << std::endl;
- }
-}
-
-std::map<std::string, bool>
-SyncLogic::getBranchPrefixes() const
-{
- std::map<std::string, bool> m;
-
- BOOST_FOREACH (const boost::shared_ptr<Sync::Leaf> leaf, m_state->getLeaves ())
- {
- std::string prefix = leaf->getInfo()->toString();
- // do not return forwarder prefix
- if (prefix != forwarderPrefix)
- {
- m.insert(std::pair<std::string, bool>(prefix, false));
- }
- }
-
- return m;
-}
-
-}
diff --git a/src/sync-logic.h b/src/sync-logic.h
deleted file mode 100644
index 181979b..0000000
--- a/src/sync-logic.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/web/index.html>
- */
-
-#ifndef SYNC_LOGIC_H
-#define SYNC_LOGIC_H
-
-#include "boost-header.h"
-#include <memory>
-#include <map>
-
-#include <ndn-cxx/face.hpp>
-#include <ndn-cxx/security/validator.hpp>
-#include <ndn-cxx/security/key-chain.hpp>
-#include <ndn-cxx/util/scheduler.hpp>
-
-#include "sync-interest-table.h"
-#include "sync-diff-state.h"
-#include "sync-full-state.h"
-#include "sync-std-name-info.h"
-
-#include "sync-diff-state-container.h"
-
-#ifdef _DEBUG
-#ifdef HAVE_LOG4CXX
-#include <log4cxx/logger.h>
-#endif
-#endif
-
-namespace Sync {
-
-struct MissingDataInfo {
- std::string prefix;
- SeqNo low;
- SeqNo high;
-};
-
-/**
- * \ingroup sync
- * @brief A wrapper for SyncApp, which handles ccnx related things (process
- * interests and data)
- */
-
-class SyncLogic
-{
-public:
- //typedef boost::function< void ( const std::string &/*prefix*/, const SeqNo &/*newSeq*/, const SeqNo &/*oldSeq*/ ) > LogicUpdateCallback;
- typedef boost::function< void (const std::vector<MissingDataInfo> & ) > LogicUpdateCallback;
- typedef boost::function< void (const std::string &/*prefix*/ ) > LogicRemoveCallback;
- typedef boost::function< void (const std::string &)> LogicPerBranchCallback;
-
- SyncLogic (const ndn::Name& syncPrefix,
- const ndn::IdentityCertificate& myCertificate,
- ndn::shared_ptr<ndn::Validator> validator,
- ndn::shared_ptr<ndn::Face> face,
- LogicUpdateCallback onUpdate,
- LogicRemoveCallback onRemove);
-
- SyncLogic (const ndn::Name& syncPrefix,
- const ndn::IdentityCertificate& myCertificate,
- ndn::shared_ptr<ndn::Validator> validator,
- ndn::shared_ptr<ndn::Face> face,
- LogicPerBranchCallback onUpdateBranch);
-
- ~SyncLogic ();
-
- /**
- * a wrapper for the same func in SyncApp
- */
- void addLocalNames (const ndn::Name &prefix, uint64_t session, uint64_t seq);
-
- /**
- * @brief remove a participant's subtree from the sync tree
- * @param prefix the name prefix for the participant
- */
- void remove (const ndn::Name &prefix);
-
- std::string
- getRootDigest();
-
-#ifdef _DEBUG
- ndn::Scheduler &
- getScheduler () { return m_scheduler; }
-#endif
-
- void
- printState () const;
-
- std::map<std::string, bool>
- getBranchPrefixes() const;
-
-private:
- void
- delayedChecksLoop ();
-
- void
- onSyncInterest (const ndn::Name& prefix, const ndn::Interest& interest);
-
- void
- onSyncRegisterFailed(const ndn::Name& prefix, const std::string& msg);
-
- void
- onSyncData(const ndn::Interest& interest, ndn::Data& data);
-
- void
- onSyncTimeout(const ndn::Interest& interest);
-
- void
- onSyncDataValidationFailed(const ndn::shared_ptr<const ndn::Data>& data);
-
- void
- onSyncDataValidated(const ndn::shared_ptr<const ndn::Data>& data);
-
- void
- processSyncInterest (const ndn::Name &name,
- DigestConstPtr digest, bool timedProcessing=false);
-
- void
- processSyncData (const ndn::Name &name,
- DigestConstPtr digest, const char *wireData, size_t len);
-
- void
- processSyncRecoveryInterest (const ndn::Name &name,
- DigestConstPtr digest);
-
- void
- insertToDiffLog (DiffStatePtr diff);
-
- void
- satisfyPendingSyncInterests (DiffStateConstPtr diff);
-
- boost::tuple<DigestConstPtr, std::string>
- convertNameToDigestAndType (const ndn::Name &name);
-
- void
- sendSyncInterest ();
-
- void
- sendSyncRecoveryInterests (DigestConstPtr digest);
-
- void
- sendSyncData (const ndn::Name &name,
- DigestConstPtr digest, StateConstPtr state);
-
- void
- sendSyncData (const ndn::Name &name,
- DigestConstPtr digest, SyncStateMsg &msg);
-
- size_t
- getNumberOfBranches () const;
-
-private:
- FullStatePtr m_state;
- DiffStateContainer m_log;
-
- ndn::Name m_outstandingInterestName;
- SyncInterestTable m_syncInterestTable;
-
- ndn::Name m_syncPrefix;
- ndn::IdentityCertificate m_myCertificate;
- LogicUpdateCallback m_onUpdate;
- LogicRemoveCallback m_onRemove;
- LogicPerBranchCallback m_onUpdateBranch;
- bool m_perBranch;
- ndn::shared_ptr<ndn::Validator> m_validator;
- ndn::KeyChain m_keyChain;
- ndn::shared_ptr<ndn::Face> m_face;
- const ndn::RegisteredPrefixId* m_syncRegisteredPrefixId;
-
- ndn::Scheduler m_scheduler;
-
- boost::mt19937 m_randomGenerator;
- boost::variate_generator<boost::mt19937&, boost::uniform_int<> > m_rangeUniformRandom;
- boost::variate_generator<boost::mt19937&, boost::uniform_int<> > m_reexpressionJitter;
-
- static const int m_unknownDigestStoreTime = 10; // seconds
- static const int m_syncResponseFreshness; // MUST BE dividable by 1000!!!
- static const int m_syncInterestReexpress; // seconds
-
- static const int m_defaultRecoveryRetransmitInterval = 200; // milliseconds
- uint32_t m_recoveryRetransmissionInterval; // milliseconds
-
- ndn::EventId m_delayedInterestProcessingId;
- ndn::EventId m_reexpressingInterestId;
- ndn::EventId m_reexpressingRecoveryInterestId;
-
- std::string m_instanceId;
- static int m_instanceCounter;
-};
-
-
-} // Sync
-
-#endif // SYNC_APP_WRAPPER_H
diff --git a/src/sync-name-info.h b/src/sync-name-info.h
deleted file mode 100644
index 2932b6e..0000000
--- a/src/sync-name-info.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_NAME_INFO_H
-#define SYNC_NAME_INFO_H
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-#include <map>
-#include <string>
-#include "sync-digest.h"
-
-namespace Sync {
-
-/**
- * @ingroup sync
- * @brief Templated class for the leaf name
- */
-class NameInfo
-{
-private:
- typedef boost::weak_ptr<const NameInfo> const_weak_ptr;
-
-public:
- virtual ~NameInfo () { };
-
- /**
- * @brief Get ID of the record (should be locally-unique, but not really necessary---this is be used for hashing purposes)
- */
- size_t
- getHashId () const { return m_id; }
-
- /**
- * @brief Check if two names are equal
- * @param info name to check with
- */
- virtual bool
- operator == (const NameInfo &info) const = 0;
-
- /**
- * @brief Check if two names are in order
- * @param info name to check with
- */
- virtual bool
- operator < (const NameInfo &info) const = 0;
-
- /**
- * @brief Calculates digest of the name
- */
- const Digest &
- getDigest () const { return m_digest; }
-
- /**
- * @brief Convert prefix to string
- * @returns string representation of prefix
- */
- virtual std::string
- toString () const = 0;
-
-protected:
- // actual stuff
- size_t m_id; ///< @brief Identifies NameInfo throughout the library (for hash container, doesn't need to be strictly unique)
- Digest m_digest;
-
- // static stuff
- typedef std::map<std::string, const_weak_ptr> NameMap;
- static size_t m_ids;
- static NameMap m_names;
-};
-
-typedef boost::shared_ptr<NameInfo> NameInfoPtr;
-typedef boost::shared_ptr<const NameInfo> NameInfoConstPtr;
-
-inline std::ostream &
-operator << (std::ostream &os, const NameInfo &info)
-{
- os << info.toString ();
- return os;
-}
-
-} // Sync
-
-#endif // SYNC_NAME_INFO_H
diff --git a/src/sync-seq-no.h b/src/sync-seq-no.h
deleted file mode 100644
index 7b8399f..0000000
--- a/src/sync-seq-no.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/web/index.html>
- */
-
-#ifndef SYNC_SEQ_NO_H
-#define SYNC_SEQ_NO_H
-
-#include <boost/cstdint.hpp>
-#include "sync-digest.h"
-
-namespace Sync {
-
-/**
- * @ingroup sync
- * @brief Sequence number abstraction
- */
-class SeqNo
-{
-public:
- /**
- * @brief Default constructor. Creates an zero sequence number with zero session ID (basically is an invalid object)
- */
- SeqNo ()
- : m_valid (false)
- , m_session (0)
- , m_seq (0)
- {
- }
-
- /**
- * @brief Copy constructor
- * @param seq sequence number object to copy from
- */
- SeqNo (const SeqNo &seq)
- {
- *this = seq;
- }
-
- /**
- * @brief Assignment operator
- * @param seq sequence number object to copy from
- */
- SeqNo &
- operator = (const SeqNo &seq)
- {
- m_valid = seq.m_valid;
- m_session = seq.m_session;
- m_seq = seq.m_seq;
-
- return *this;
- }
-
- /**
- * @brief Constructor with just sequence number. Session assumed to be zero
- * @param seq Sequence number
- */
- SeqNo (uint64_t seq)
- : m_valid (true)
- , m_session (0)
- , m_seq (seq)
- { }
-
- /**
- * @brief Constructor with session and sequence id
- * @param session Session ID
- * @param seq Sequence number
- */
- SeqNo (uint64_t session, uint64_t seq)
- : m_valid (true)
- , m_session (session)
- , m_seq (seq)
- { }
-
- /**
- * @brief Get sequence number digest
- *
- * Digest will be calculated every time it is requested
- */
- DigestConstPtr
- getDigest () const;
-
- /**
- * @brief Compare if one sequence number is lower
- * @param seq Another sequence number to compare with
- *
- * tuple (session1, seq1) is less than (session2, seq2) in two cases:
- * 1. session1 < session2
- * 2. session1 == session2 and seq1 < seq2
- */
- bool
- operator < (const SeqNo &seq) const
- {
- return m_session < seq.m_session || (m_session == seq.m_session && m_seq < seq.m_seq);
- }
-
- /**
- * @brief Compare if two sequence numbers are equal
- * @param seq Another sequence number to compare with
- */
- bool
- operator == (const SeqNo &seq) const
- {
- return m_session == seq.m_session && m_seq == seq.m_seq;
- }
-
- bool
- operator <= (const SeqNo &seq) const
- {
- return m_session == seq.m_session && m_seq <= seq.m_seq;
- }
-
- SeqNo &
- operator ++ ()
- {
- if (m_valid) {
- m_seq ++;
- }
- else {
- m_valid = true;
- }
- return *this;
- }
-
- bool
- isValid () const
- {
- return m_valid;
- }
-
- /**
- * @brief Get session id
- */
- uint64_t getSession () const
- { return m_session; }
-
- /**
- * @brief Get sequence number
- */
- uint64_t getSeq () const
- { return m_seq; }
-
- /**
- * @brief Set sequence number
- */
- void
- setSeq(uint64_t seq)
- { m_seq = seq; }
-
-private:
- bool m_valid;
-
- /**
- * @brief Session ID (e.g., after crash, application will choose new session ID.
- *
- * Note that session IDs for the same name should always increase. So, the good choice
- * for the session ID is client's timestamp
- */
- uint64_t m_session;
-
- /**
- * @brief Sequence number
- *
- * Sequence number for a session always starts with 0 and goes to max value.
- *
- * For now, wrapping sequence number after max to zero is not supported
- */
- uint64_t m_seq;
-};
-
-inline std::ostream &
-operator << (std::ostream &os, const SeqNo &seqno)
-{
- os << "<session>" << seqno.getSession () << "</session><seqno>" << seqno.getSeq () << "</seqno>";
- return os;
-}
-
-} // Sync
-
-#endif // SYNC_SEQ_NO_H
diff --git a/src/sync-socket.cc b/src/sync-socket.cc
deleted file mode 100644
index b344a3e..0000000
--- a/src/sync-socket.cc
+++ /dev/null
@@ -1,245 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/web/index.html>
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-socket.h"
-#include "sync-logging.h"
-
-using namespace ndn;
-
-INIT_LOGGER ("SyncSocket")
-
-namespace Sync {
-
-using ndn::shared_ptr;
-
-static const uint8_t ROUTING_PREFIX_SEPARATOR[2] = {0xF0, 0x2E};
-
-const Name SyncSocket::EMPTY_NAME = Name();
-
-SyncSocket::SyncSocket (const Name& syncPrefix,
- const Name& dataPrefix,
- uint64_t dataSession,
- bool withRoutingPrefix,
- const Name& routingPrefix,
- shared_ptr<Face> face,
- const IdentityCertificate& myCertificate,
- shared_ptr<SecRuleRelative> dataRule,
- NewDataCallback dataCallback,
- RemoveCallback rmCallback )
- : m_dataPrefix(dataPrefix)
- , m_dataSession(dataSession)
- , m_withRoutingPrefix(false)
- , m_newDataCallback(dataCallback)
- , m_myCertificate(myCertificate)
- , m_face(face)
- , m_ioService(face->getIoService())
-{
- if(withRoutingPrefix && !routingPrefix.isPrefixOf(m_dataPrefix))
- {
- m_withRoutingPrefix = true;
- m_routableDataPrefix.append(routingPrefix).append(ROUTING_PREFIX_SEPARATOR, 2).append(m_dataPrefix);
- }
-
-
- if(static_cast<bool>(dataRule))
- {
- m_withSecurity = true;
- m_syncValidator = shared_ptr<Validator>(new SyncValidator(syncPrefix,
- m_myCertificate,
- *m_face,
- bind(&SyncSocket::publishData, this, _1, _2, _3, true),
- dataRule));
- }
- else
- {
- m_withSecurity = false;
- m_syncValidator = shared_ptr<Validator>(new ValidatorNull());
- }
-
-
- m_syncLogic = shared_ptr<SyncLogic>(new SyncLogic(syncPrefix,
- myCertificate,
- m_syncValidator,
- m_face,
- bind(&SyncSocket::passCallback, this, _1),
- rmCallback));
-}
-
-SyncSocket::~SyncSocket()
-{
-}
-
-void
-SyncSocket::publishData(const uint8_t* buf, size_t len, int freshness, bool isCert)
-{
- shared_ptr<Data> data = make_shared<Data>();
- data->setContent(reinterpret_cast<const uint8_t*>(buf), len);
- data->setFreshnessPeriod(time::milliseconds(1000*freshness));
-
- m_ioService.post(bind(&SyncSocket::publishDataInternal, this,
- data, isCert));
-}
-
-void
-SyncSocket::publishDataInternal(shared_ptr<Data> data, bool isCert)
-{
- Name dataPrefix = (m_withRoutingPrefix ? m_routableDataPrefix : m_dataPrefix);
-
- uint64_t sequence = getNextSeq();
-
- Name dataName;
- dataName.append(m_dataPrefix)
- .append(boost::lexical_cast<std::string>(m_dataSession))
- .append(boost::lexical_cast<std::string>(sequence));
- if(isCert)
- dataName.append("INTRO-CERT");
- data->setName(dataName);
- m_keyChain.sign(*data, m_myCertificate.getName());
-
- if(m_withRoutingPrefix)
- {
- Name wrappedName;
- wrappedName.append(m_routableDataPrefix)
- .append(boost::lexical_cast<std::string>(m_dataSession))
- .append(boost::lexical_cast<std::string>(sequence));
-
- Data wrappedData(wrappedName);
- wrappedData.setContent(data->wireEncode());
- m_keyChain.sign(wrappedData, m_myCertificate.getName());
-
- m_face->put(wrappedData);
- }
- else
- {
- m_face->put(*data);
- }
-
- SeqNo s(m_dataSession, sequence + 1);
- m_sequenceLog[dataPrefix] = s;
- m_syncLogic->addLocalNames (dataPrefix, m_dataSession, sequence); // If DNS works, we should use pure m_dataprefix rather than the one with routing prefix.
-}
-
-void
-SyncSocket::fetchData(const Name& prefix, const SeqNo& seq, const OnDataValidated& dataCallback, int retry)
-{
- Name interestName = prefix;
- interestName.append(boost::lexical_cast<std::string>(seq.getSession())).append(boost::lexical_cast<std::string>(seq.getSeq()));
-
- ndn::Interest interest(interestName);
- interest.setMustBeFresh(true);
-
- m_face->expressInterest(interest,
- bind(&SyncSocket::onData, this, _1, _2, dataCallback),
- bind(&SyncSocket::onDataTimeout, this, _1, retry, dataCallback));
-
-}
-
-void
-SyncSocket::onData(const ndn::Interest& interest, Data& data, const OnDataValidated& dataCallback)
-{
- bool encaped = false;
-
- Name interestName = interest.getName();
- Name::const_iterator it = interestName.begin();
- Name::const_iterator end = interestName.end();
-
- size_t offset = interestName.size();
- for(; it != end; it++)
- {
- offset--;
- if(it->toUri() == "%F0.")
- {
- encaped = true;
- break;
- }
- }
-
- if(!encaped)
- offset = interestName.size();
-
- const OnDataValidated& onValidated = bind(&SyncSocket::onDataValidated, this, _1, offset, dataCallback);
- const OnDataValidationFailed& onValidationFailed = bind(&SyncSocket::onDataValidationFailed, this, _1, _2);
-
- if(encaped)
- {
- shared_ptr<Data> innerData = make_shared<Data>();
- innerData->wireDecode(data.getContent().blockFromValue());
- m_syncValidator->validate(*innerData, onValidated, onValidationFailed);
- }
- else
- m_syncValidator->validate(data, onValidated, onValidationFailed);
-}
-
-void
-SyncSocket::onDataTimeout(const ndn::Interest& interest, int retry, const OnDataValidated& dataCallback)
-{
- if(retry > 0)
- {
- m_face->expressInterest(interest,
- bind(&SyncSocket::onData,
- this,
- _1,
- _2,
- dataCallback),
- bind(&SyncSocket::onDataTimeout,
- this,
- _1,
- retry - 1,
- dataCallback));
-
- }
- else
- _LOG_DEBUG("interest eventually time out!");
-}
-
-void
-SyncSocket::onDataValidated(const shared_ptr<const Data>& data,
- size_t interestNameSize,
- const OnDataValidated& onValidated)
-{
- if(data->getName().size() > interestNameSize
- && data->getName().get(interestNameSize).toUri() == "INTRO-CERT")
- {
- if(!m_withSecurity)
- return;
-
- Data rawData;
- rawData.wireDecode(data->getContent().blockFromValue());
- IntroCertificate introCert(rawData);
- dynamic_pointer_cast<SyncValidator>(m_syncValidator)->addParticipant(introCert);
- }
- else
- {
- onValidated(data);
- }
-}
-
-void
-SyncSocket::onDataValidationFailed(const shared_ptr<const Data>& data,
- const std::string& failureInfo)
-{
- _LOG_DEBUG("data cannot be verified!: " << failureInfo);
-}
-
-}//Sync
diff --git a/src/sync-socket.h b/src/sync-socket.h
deleted file mode 100644
index 5e66ac6..0000000
--- a/src/sync-socket.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/web/index.html>
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef _SYNC_SOCKET_H
-#define _SYNC_SOCKET_H
-
-#include <ndn-cxx/face.hpp>
-#include <ndn-cxx/security/validator.hpp>
-#include <ndn-cxx/security/validator-null.hpp>
-#include <ndn-cxx/security/key-chain.hpp>
-
-#include "sync-logic.h"
-#include "sync-seq-no.h"
-#include "sync-validator.h"
-
-#include <utility>
-#include <map>
-#include <vector>
-#include <sstream>
-
-namespace Sync {
-
-/**
- * \ingroup sync
- * @brief A simple interface to interact with client code
- */
-class SyncSocket
-{
-public:
- struct Error : public std::runtime_error { Error(const std::string &what) : std::runtime_error(what) {} };
-
- typedef ndn::function< void (const std::vector<MissingDataInfo> &, SyncSocket * ) > NewDataCallback;
- typedef ndn::function< void (const std::string &/*prefix*/ ) > RemoveCallback;
-
- static const ndn::Name EMPTY_NAME;
-
- SyncSocket(const ndn::Name& syncPrefix,
- const ndn::Name& dataPrefix,
- uint64_t dataSession,
- bool withRoutingPrefix,
- const ndn::Name& routingPrefix,
- ndn::shared_ptr<ndn::Face> face,
- const ndn::IdentityCertificate& myCertificate,
- ndn::shared_ptr<ndn::SecRuleRelative> dataRule,
- NewDataCallback dataCallback,
- RemoveCallback rmCallback);
-
- ~SyncSocket();
-
- void
- publishData(const uint8_t* buf, size_t len, int freshness, bool isCert = false);
-
- void
- leave()
- {
- m_syncLogic->remove(m_withRoutingPrefix ? m_routableDataPrefix : m_dataPrefix);
- }
-
- void
- remove(const ndn::Name& prefix)
- {
- m_syncLogic->remove(prefix);
- }
-
- void
- fetchData(const ndn::Name &prefix, const SeqNo &seq, const ndn::OnDataValidated& onValidated, int retry = 0);
-
- std::string
- getRootDigest()
- {
- return m_syncLogic->getRootDigest();
- }
-
- uint64_t
- getNextSeq()
- {
- // If DNS works, we should use pure m_dataprefix rather than the one with routing prefix.
- SequenceLog::iterator i = m_sequenceLog.find (m_withRoutingPrefix ? m_routableDataPrefix : m_dataPrefix);
-
- if (i != m_sequenceLog.end ())
- {
- SeqNo s = i->second;
- if (s.getSession() == m_dataSession)
- return s.getSeq();
- }
- return 0;
- }
-
- SyncLogic &
- getLogic()
- {
- return *m_syncLogic;
- }
-
- void
- addParticipant(const ndn::IdentityCertificate& introducee)
- {
- if(m_withSecurity)
- {
- ndn::dynamic_pointer_cast<SyncValidator>(m_syncValidator)->addParticipant(introducee);
- }
- }
-
- void
- addParticipant(const IntroCertificate& introCert)
- {
- if(m_withSecurity)
- {
- ndn::dynamic_pointer_cast<SyncValidator>(m_syncValidator)->addParticipant(introCert);
- }
- }
-
- void
- getIntroCertNames(std::vector<ndn::Name>& list)
- {
- if(m_withSecurity)
- {
- ndn::dynamic_pointer_cast<SyncValidator>(m_syncValidator)->getIntroCertNames(list);
- }
- }
-
- const IntroCertificate&
- getIntroCertificate(const ndn::Name& name)
- {
- if(m_withSecurity)
- {
- return ndn::dynamic_pointer_cast<SyncValidator>(m_syncValidator)->getIntroCertificate(name);
- }
- throw Error("You are running SyncSocket without security!");
- }
-
- // // make this a static function so we don't have to create socket instance without
- // // knowing the local prefix. it's a wrong place for this function anyway
- // static std::string
- // GetLocalPrefix ();
-
-private:
- void
- publishDataInternal(ndn::shared_ptr<ndn::Data> data, bool isCert);
-
- void
- passCallback(const std::vector<MissingDataInfo> &v)
- {
- m_newDataCallback(v, this);
- }
-
- void
- onData(const ndn::Interest& interest, ndn::Data& data, const ndn::OnDataValidated& dataCallback);
-
- void
- onDataTimeout(const ndn::Interest& interest, int retry, const ndn::OnDataValidated& dataCallback);
-
-
- void
- onDataValidated(const ndn::shared_ptr<const ndn::Data>& data,
- size_t interestNameSize,
- const ndn::OnDataValidated& onValidated);
-
- void
- onDataValidationFailed(const ndn::shared_ptr<const ndn::Data>& data,
- const std::string& failureInfo);
-
-private:
- typedef std::map<ndn::Name, SeqNo> SequenceLog;
-
- ndn::Name m_dataPrefix;
- uint64_t m_dataSession;
- ndn::Name m_routableDataPrefix;
- bool m_withRoutingPrefix;
- NewDataCallback m_newDataCallback;
- SequenceLog m_sequenceLog;
- ndn::IdentityCertificate m_myCertificate;
- ndn::KeyChain m_keyChain;
- ndn::shared_ptr<ndn::Face> m_face;
- boost::asio::io_service& m_ioService;
- bool m_withSecurity;
- ndn::shared_ptr<ndn::Validator> m_syncValidator;
- ndn::shared_ptr<SyncLogic> m_syncLogic;
-};
-
-} // Sync
-
-#endif // SYNC_SOCKET_H
diff --git a/src/sync-state-leaf-container.h b/src/sync-state-leaf-container.h
deleted file mode 100644
index d6d982a..0000000
--- a/src/sync-state-leaf-container.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_STATE_LEAF_CONTAINER
-#define SYNC_STATE_LEAF_CONTAINER
-
-#include "sync-leaf.h"
-#include "sync-name-info.h"
-
-#include <boost/multi_index_container.hpp>
-// #include <boost/multi_index/tag.hpp>
-#include <boost/multi_index/ordered_index.hpp>
-// #include <boost/multi_index/composite_key.hpp>
-#include <boost/multi_index/hashed_index.hpp>
-// #include <boost/multi_index/random_access_index.hpp>
-// #include <boost/multi_index/member.hpp>
-#include <boost/multi_index/mem_fun.hpp>
-
-namespace mi = boost::multi_index;
-
-namespace Sync {
-
-struct NameInfoHash : public std::unary_function<NameInfo, std::size_t>
-{
- std::size_t
- operator() (NameInfoConstPtr prefix) const
- {
- return prefix->getHashId ();
- }
-};
-
-struct NameInfoEqual : public std::unary_function<NameInfo, std::size_t>
-{
- bool
- operator() (NameInfoConstPtr prefix1, NameInfoConstPtr prefix2) const
- {
- return *prefix1 == *prefix2;
- }
-};
-
-struct NameInfoCompare : public std::unary_function<NameInfo, std::size_t>
-{
- bool
- operator() (NameInfoConstPtr prefix1, NameInfoConstPtr prefix2) const
- {
- return *prefix1 < *prefix2;
- }
-};
-
-/// @cond include_hidden
-struct hashed { };
-struct ordered { };
-/// @endcond
-
-/**
- * \ingroup sync
- * @brief Container for SYNC leaves
- */
-struct LeafContainer : public mi::multi_index_container<
- LeafPtr,
- mi::indexed_by<
- // For fast access to elements using NameInfo
- mi::hashed_unique<
- mi::tag<hashed>,
- mi::const_mem_fun<Leaf, NameInfoConstPtr, &Leaf::getInfo>,
- NameInfoHash,
- NameInfoEqual
- >,
-
- mi::ordered_unique<
- mi::tag<ordered>,
- mi::const_mem_fun<Leaf, NameInfoConstPtr, &Leaf::getInfo>,
- NameInfoCompare
- >
- >
- >
-{
-};
-
-} // Sync
-
-#endif // SYNC_STATE_LEAF_CONTAINER
diff --git a/src/sync-state.cc b/src/sync-state.cc
deleted file mode 100644
index efb53c7..0000000
--- a/src/sync-state.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-state.h"
-#include "sync-diff-leaf.h"
-#include "sync-std-name-info.h"
-
-#include <boost/assert.hpp>
-#include <boost/foreach.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/throw_exception.hpp>
-#include <boost/lexical_cast.hpp>
-
-// using namespace std;
-using namespace boost;
-
-typedef error_info<struct tag_errmsg, std::string> info_str;
-
-using namespace Sync::Error;
-
-namespace Sync {
-
-/*
-std::ostream &
-operator << (std::ostream &os, const State &state)
-{
- os << "<state>"; DEBUG_ENDL;
-
- BOOST_FOREACH (shared_ptr<const Leaf> leaf, state.getLeaves ().get<ordered> ())
- {
- shared_ptr<const DiffLeaf> diffLeaf = dynamic_pointer_cast<const DiffLeaf> (leaf);
- if (diffLeaf != 0)
- {
- os << "<item action=\"" << diffLeaf->getOperation () << "\">"; DEBUG_ENDL;
- }
- else
- {
- os << "<item>"; DEBUG_ENDL;
- }
- os << "<name>" << *leaf->getInfo () << "</name>"; DEBUG_ENDL;
- if (diffLeaf == 0 || (diffLeaf != 0 && diffLeaf->getOperation () == UPDATE))
- {
- os << "<seq>" << leaf->getSeq () << "</seq>"; DEBUG_ENDL;
- }
- os << "</item>"; DEBUG_ENDL;
- }
- os << "</state>";
-}
-*/
-
-SyncStateMsg &
-operator << (SyncStateMsg &ossm, const State &state)
-{
- BOOST_FOREACH (shared_ptr<const Leaf> leaf, state.getLeaves ().get<ordered> ())
- {
- SyncState *oss = ossm.add_ss();
- shared_ptr<const DiffLeaf> diffLeaf = dynamic_pointer_cast<const DiffLeaf> (leaf);
- if (diffLeaf != 0 && diffLeaf->getOperation() != UPDATE)
- {
- oss->set_type(SyncState::DELETE);
- }
- else
- {
- oss->set_type(SyncState::UPDATE);
- }
-
- std::ostringstream os;
- os << *leaf->getInfo();
- oss->set_name(os.str());
-
- if (diffLeaf == 0 || (diffLeaf != 0 && diffLeaf->getOperation () == UPDATE))
- {
- SyncState::SeqNo *seqNo = oss->mutable_seqno();
- seqNo->set_session(leaf->getSeq().getSession());
- seqNo->set_seq(leaf->getSeq().getSeq());
- }
- }
- return ossm;
-}
-
-/*
-std::istream &
-operator >> (std::istream &in, State &state)
-{
- TiXmlDocument doc;
- in >> doc;
-
- if (doc.RootElement() == 0)
- BOOST_THROW_EXCEPTION (SyncXmlDecodingFailure () << info_str ("Empty XML"));
-
- for (TiXmlElement *iterator = doc.RootElement()->FirstChildElement ("item");
- iterator != 0;
- iterator = iterator->NextSiblingElement("item"))
- {
- TiXmlElement *name = iterator->FirstChildElement ("name");
- if (name == 0 || name->GetText() == 0)
- BOOST_THROW_EXCEPTION (SyncXmlDecodingFailure () << info_str ("<name> element is missing"));
-
- NameInfoConstPtr info = StdNameInfo::FindOrCreate (name->GetText());
-
- if (iterator->Attribute("action") == 0 || strcmp(iterator->Attribute("action"), "update") == 0)
- {
- TiXmlElement *seq = iterator->FirstChildElement ("seq");
- if (seq == 0)
- BOOST_THROW_EXCEPTION (SyncXmlDecodingFailure () << info_str ("<seq> element is missing"));
-
- TiXmlElement *session = seq->FirstChildElement ("session");
- TiXmlElement *seqno = seq->FirstChildElement ("seqno");
-
- if (session == 0 || session->GetText() == 0)
- BOOST_THROW_EXCEPTION (SyncXmlDecodingFailure () << info_str ("<session> element is missing"));
- if (seqno == 0 || seqno->GetText() == 0)
- BOOST_THROW_EXCEPTION (SyncXmlDecodingFailure () << info_str ("<seqno> element is missing"));
-
- state.update (info, SeqNo (
- lexical_cast<uint32_t> (session->GetText()),
- lexical_cast<uint32_t> (seqno->GetText())
- ));
- }
- else
- {
- state.remove (info);
- }
- }
-
- return in;
-}
-*/
-
-SyncStateMsg &
-operator >> (SyncStateMsg &issm, State &state)
-{
- int n = issm.ss_size();
- for (int i = 0; i < n; i++)
- {
- const SyncState &ss = issm.ss(i);
- NameInfoConstPtr info = StdNameInfo::FindOrCreate (ss.name());
- if (ss.type() == SyncState::UPDATE)
- {
- uint64_t session = lexical_cast<uint64_t>(ss.seqno().session());
- uint64_t seq = lexical_cast<uint64_t>(ss.seqno().seq());
- SeqNo seqNo(session, seq);
- state.update(info, seqNo);
- }
- else
- {
- state.remove(info);
- }
- }
- return issm;
-}
-
-}
diff --git a/src/sync-state.h b/src/sync-state.h
deleted file mode 100644
index 4119c95..0000000
--- a/src/sync-state.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_STATE_H
-#define SYNC_STATE_H
-
-#include "sync-state-leaf-container.h"
-#include <boost/exception/all.hpp>
-#include "boost/tuple/tuple.hpp"
-#include "sync-state.pb.h"
-
-/**
- * \defgroup sync SYNC protocol
- *
- * Implementation of SYNC protocol
- */
-namespace Sync {
-
-/**
- * \ingroup sync
- * @brief this prefix will be used for the dummy node which increases its sequence number whenever
- * a remove operation happens; this is to prevent the reversion of root digest when we prune
- * a branch, i.e. help the root digest to be forward only
- * No corresponding data msg would be published and no attempt would be made to retrieve the
- * data msg
- */
-const std::string forwarderPrefix = "/d0n0t18ak/t0ps8cr8t";
-
-class State;
-typedef boost::shared_ptr<State> StatePtr;
-typedef boost::shared_ptr<State> StateConstPtr;
-
-/**
- * \ingroup sync
- * @brief Container for state leaves and definition of the abstract interface to work with State objects
- */
-class State
-{
-public:
- virtual ~State () { };
-
- /**
- * @brief Add or update leaf to the state tree
- *
- * @param info name of the leaf
- * @param seq sequence number of the leaf
- */
- virtual boost::tuple<bool/*inserted*/, bool/*updated*/, SeqNo/*oldSeqNo*/>
- update (NameInfoConstPtr info, const SeqNo &seq) = 0;
-
- /**
- * @brief Remove leaf from the state tree
- * @param info name of the leaf
- */
- virtual bool
- remove (NameInfoConstPtr info) = 0;
-
- /**
- * @brief Get state leaves
- */
- const LeafContainer &
- getLeaves () const
- { return m_leaves; }
-
-protected:
- LeafContainer m_leaves;
-};
-
-
-/**
- * @brief Formats a protobuf SyncStateMsg msg
- * @param oss output SyncStateMsg msg
- * @param state state
- * @returns output SyncStateMsg msg
- */
-SyncStateMsg &
-operator << (SyncStateMsg &ossm, const State &state);
-
-
-/**
- * @brief Parse a protobuf SyncStateMsg msg
- * @param iss input SyncStateMsg msg
- * @param state state
- * @returns SyncStateMsg msg
- */
-SyncStateMsg &
-operator >> (SyncStateMsg &issm, State &state);
-
-namespace Error {
-/**
- * @brief Will be thrown when data cannot be properly decoded to SyncStateMsg
- */
-struct SyncStateMsgDecodingFailure : virtual boost::exception, virtual std::exception { };
-}
-
-} // Sync
-
-#endif // SYNC_STATE_H
diff --git a/src/sync-state.proto b/src/sync-state.proto
deleted file mode 100644
index 7cc6b18..0000000
--- a/src/sync-state.proto
+++ /dev/null
@@ -1,24 +0,0 @@
-package Sync;
-
-message SyncState
-{
- required string name = 1;
- enum ActionType
- {
- UPDATE = 0;
- DELETE = 1;
- OTHER = 2;
- }
- required ActionType type = 2;
- message SeqNo
- {
- required uint64 seq = 1;
- required uint64 session = 2;
- }
- optional SeqNo seqno = 3;
-}
-
-message SyncStateMsg
-{
- repeated SyncState ss = 1;
-}
diff --git a/src/sync-std-name-info.cc b/src/sync-std-name-info.cc
deleted file mode 100644
index bc4e80f..0000000
--- a/src/sync-std-name-info.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#include "sync-std-name-info.h"
-
-// using namespace std;
-using namespace boost;
-
-namespace Sync {
-
-
-NameInfoConstPtr
-StdNameInfo::FindOrCreate (const std::string &key)
-{
- // std::cout << "FindOrCreate: " << m_names.size () << "\n";
-
- NameInfoConstPtr ret;
-
- NameMap::iterator item = m_names.find (key);
- if (item != m_names.end ())
- {
- ret = item->second.lock ();
- BOOST_ASSERT (ret != 0);
- }
- else
- {
- ret = NameInfoPtr (new StdNameInfo (key));
- weak_ptr<const NameInfo> value (ret);
- std::pair<NameMap::iterator,bool> inserted =
- m_names.insert (make_pair (key, value));
-
- BOOST_ASSERT (inserted.second); // previous call has to insert value
- item = inserted.first;
- }
-
- return ret;
-}
-
-StdNameInfo::StdNameInfo (const std::string &name)
- : m_name (name)
-{
- m_id = m_ids ++; // set ID for a newly inserted element
- m_digest << name;
- m_digest.finalize ();
-
- // std::cout << "StdNameInfo: " << name << " = " << m_id << "\n";
-}
-
-StdNameInfo::~StdNameInfo ()
-{
- // cout << "Destructor for " << m_name << "\n";
- m_names.erase (toString ());
-}
-
-std::string
-StdNameInfo::toString () const
-{
- return m_name;
-}
-
-bool
-StdNameInfo::operator == (const NameInfo &info) const
-{
- return m_name == dynamic_cast<const StdNameInfo&> (info).m_name;
-}
-
-bool
-StdNameInfo::operator < (const NameInfo &info) const
-{
- return m_name < dynamic_cast<const StdNameInfo&> (info).m_name;
-}
-
-} // Sync
diff --git a/src/sync-std-name-info.h b/src/sync-std-name-info.h
deleted file mode 100644
index 957829f..0000000
--- a/src/sync-std-name-info.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
- */
-
-#ifndef SYNC_STD_NAME_INFO_H
-#define SYNC_STD_NAME_INFO_H
-
-#include "sync-name-info.h"
-#include <string>
-
-namespace Sync {
-
-class StdNameInfo : public NameInfo
-{
-public:
- /**
- * @brief Lookup existing or create new NameInfo object
- * @param name routable prefix
- */
- static NameInfoConstPtr
- FindOrCreate (const std::string &name);
-
- /**
- * @brief Destructor which will clean up m_names structure
- */
- virtual ~StdNameInfo ();
-
- // from NameInfo
- virtual bool
- operator == (const NameInfo &info) const;
-
- virtual bool
- operator < (const NameInfo &info) const;
-
- virtual std::string
- toString () const;
-
-private:
- // implementing a singleton pattern.
- /**
- * @brief Disabled default constructor. NameInfo object should be created through FindOrCreate static call.
- */
-
- /**
- * @brief Disabled default
- */
- StdNameInfo () {}
- StdNameInfo& operator = (const StdNameInfo &info) { (void)info; return *this; }
- StdNameInfo (const std::string &name);
-
- std::string m_name;
-};
-
-} // Sync
-
-#endif // SYNC_CCNX_NAME_INFO_H
diff --git a/src/sync-name-info.cc b/src/tlv.hpp
similarity index 70%
copy from src/sync-name-info.cc
copy to src/tlv.hpp
index eb7411a..6b5f48a 100644
--- a/src/sync-name-info.cc
+++ b/src/tlv.hpp
@@ -16,18 +16,26 @@
* You should have received a copy of the GNU General Public License along with
* ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*
- * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
- * @author Chaoyi Bian <bcy@pku.edu.cn>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ * @author Yingdi Yu <yingdi@cs.ucla.edu>
*/
-#include "sync-name-info.h"
+#ifndef CHRONOSYNC_TLV_HPP
+#define CHRONOSYNC_TLV_HPP
-// #include <boost/lexical_cast.hpp>
+namespace chronosync {
+namespace tlv {
-namespace Sync {
+/**
+ * @brief Type value of sync reply related TLVs
+ * @sa docs/design.rst
+ */
+enum {
+ SyncReply = 128, // 0x80
+ StateLeaf = 129, // 0x81
+ SeqNo = 130 // 0x82
+};
-NameInfo::NameMap NameInfo::m_names;
-size_t NameInfo::m_ids = 0;
+} // namespace tlv
+} // namespace chronosync
-} // Sync
+#endif // CHRONOSYNC_TLV_HPP
diff --git a/tests/integrated-tests/test-logic.cpp b/tests/integrated-tests/test-logic.cpp
new file mode 100644
index 0000000..088b8fb
--- /dev/null
+++ b/tests/integrated-tests/test-logic.cpp
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "logic.hpp"
+
+#include "boost-test.hpp"
+
+namespace chronosync {
+namespace test {
+
+using std::vector;
+
+class Handler
+{
+public:
+ Handler(ndn::Face& face,
+ const Name& syncPrefix,
+ const Name& userPrefix)
+ : logic(face,
+ syncPrefix,
+ userPrefix,
+ bind(&Handler::onUpdate, this, _1))
+ {
+ }
+
+ void
+ onUpdate(const vector<MissingDataInfo>& v)
+ {
+ for (size_t i = 0; i < v.size(); i++) {
+ update(v[i].session, v[i].high, v[i].low);
+ }
+ }
+
+ void
+ update(const Name& p, const SeqNo& high, const SeqNo& low)
+ {
+ map[p] = high;
+ }
+
+ void
+ updateSeqNo(const SeqNo& seqNo)
+ {
+ logic.updateSeqNo(seqNo);
+ }
+
+ void
+ check(const Name& sessionName, const SeqNo& seqNo)
+ {
+ BOOST_CHECK_EQUAL(map[sessionName], seqNo);
+ }
+
+ Logic logic;
+ std::map<Name, SeqNo> map;
+};
+
+class LogicFixture
+{
+public:
+ LogicFixture()
+ : syncPrefix("/ndn/broadcast/sync")
+ , scheduler(io)
+ {
+ syncPrefix.appendVersion();
+ userPrefix[0] = Name("/user0");
+ userPrefix[1] = Name("/user1");
+ userPrefix[2] = Name("/user2");
+
+ faces[0] = make_shared<ndn::Face>(ref(io));
+ faces[1] = make_shared<ndn::Face>(ref(io));
+ faces[2] = make_shared<ndn::Face>(ref(io));
+ }
+
+ void
+ createHandler(size_t idx)
+ {
+ handler[idx] = make_shared<Handler>(ref(*faces[idx]), syncPrefix, userPrefix[idx]);
+ }
+
+ void
+ updateSeqNo(size_t idx, const SeqNo& seqNo)
+ {
+ handler[idx]->updateSeqNo(seqNo);
+ }
+
+ void
+ checkSeqNo(size_t sIdx, size_t dIdx, const SeqNo& seqNo)
+ {
+ handler[sIdx]->check(handler[dIdx]->logic.getSessionName(), seqNo);
+ }
+
+ void
+ terminate()
+ {
+ io.stop();
+ }
+
+ Name syncPrefix;
+ Name userPrefix[3];
+
+ boost::asio::io_service io;
+ shared_ptr<ndn::Face> faces[3];
+ ndn::Scheduler scheduler;
+ shared_ptr<Handler> handler[3];
+};
+
+BOOST_FIXTURE_TEST_SUITE(LogicTests, LogicFixture)
+
+void
+onUpdate(const vector<MissingDataInfo>& v)
+{
+}
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ Name syncPrefix("/ndn/broadcast/sync");
+ Name userPrefix("/user");
+ ndn::Face face;
+ BOOST_REQUIRE_NO_THROW(Logic(face, syncPrefix, userPrefix,
+ bind(onUpdate, _1)));
+}
+
+BOOST_AUTO_TEST_CASE(TwoBasic)
+{
+ scheduler.scheduleEvent(ndn::time::milliseconds(100),
+ bind(&LogicFixture::createHandler, this, 0));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(200),
+ bind(&LogicFixture::createHandler, this, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(300),
+ bind(&LogicFixture::updateSeqNo, this, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1000),
+ bind(&LogicFixture::checkSeqNo, this, 1, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1100),
+ bind(&LogicFixture::updateSeqNo, this, 0, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1800),
+ bind(&LogicFixture::checkSeqNo, this, 1, 0, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1900),
+ bind(&LogicFixture::updateSeqNo, this, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2600),
+ bind(&LogicFixture::checkSeqNo, this, 0, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2800),
+ bind(&LogicFixture::terminate, this));
+
+ io.run();
+}
+
+BOOST_AUTO_TEST_CASE(ThreeBasic)
+{
+ scheduler.scheduleEvent(ndn::time::milliseconds(100),
+ bind(&LogicFixture::createHandler, this, 0));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(200),
+ bind(&LogicFixture::createHandler, this, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(300),
+ bind(&LogicFixture::createHandler, this, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(500),
+ bind(&LogicFixture::updateSeqNo, this, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1400),
+ bind(&LogicFixture::checkSeqNo, this, 1, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1450),
+ bind(&LogicFixture::checkSeqNo, this, 2, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1500),
+ bind(&LogicFixture::updateSeqNo, this, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2400),
+ bind(&LogicFixture::checkSeqNo, this, 0, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2450),
+ bind(&LogicFixture::checkSeqNo, this, 2, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2500),
+ bind(&LogicFixture::updateSeqNo, this, 2, 4));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(4400),
+ bind(&LogicFixture::checkSeqNo, this, 0, 2, 4));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(4450),
+ bind(&LogicFixture::checkSeqNo, this, 1, 2, 4));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(4500),
+ bind(&LogicFixture::terminate, this));
+
+ io.run();
+}
+
+BOOST_AUTO_TEST_CASE(ResetRecover)
+{
+ scheduler.scheduleEvent(ndn::time::milliseconds(100),
+ bind(&LogicFixture::createHandler, this, 0));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(200),
+ bind(&LogicFixture::createHandler, this, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(500),
+ bind(&LogicFixture::updateSeqNo, this, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1400),
+ bind(&LogicFixture::checkSeqNo, this, 1, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1500),
+ bind(&LogicFixture::updateSeqNo, this, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2400),
+ bind(&LogicFixture::checkSeqNo, this, 0, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2500),
+ bind(&LogicFixture::createHandler, this, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(3000),
+ bind(&LogicFixture::checkSeqNo, this, 1, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(3050),
+ bind(&LogicFixture::checkSeqNo, this, 0, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(3100),
+ bind(&LogicFixture::updateSeqNo, this, 2, 4));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(4000),
+ bind(&LogicFixture::checkSeqNo, this, 1, 2, 4));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(4050),
+ bind(&LogicFixture::checkSeqNo, this, 0, 2, 4));
+
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(4500),
+ bind(&LogicFixture::terminate, this));
+
+ io.run();
+}
+
+BOOST_AUTO_TEST_CASE(RecoverConflict)
+{
+ scheduler.scheduleEvent(ndn::time::milliseconds(0),
+ bind(&LogicFixture::createHandler, this, 0));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(50),
+ bind(&LogicFixture::createHandler, this, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(100),
+ bind(&LogicFixture::createHandler, this, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(500),
+ bind(&LogicFixture::updateSeqNo, this, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1400),
+ bind(&LogicFixture::checkSeqNo, this, 1, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1400),
+ bind(&LogicFixture::checkSeqNo, this, 2, 0, 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1500),
+ bind(&LogicFixture::updateSeqNo, this, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(1500),
+ bind(&LogicFixture::updateSeqNo, this, 2, 4));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2400),
+ bind(&LogicFixture::checkSeqNo, this, 0, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2450),
+ bind(&LogicFixture::checkSeqNo, this, 0, 2, 4));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2500),
+ bind(&LogicFixture::checkSeqNo, this, 1, 2, 4));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(2550),
+ bind(&LogicFixture::checkSeqNo, this, 2, 1, 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(4500),
+ bind(&LogicFixture::terminate, this));
+
+ io.run();
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace chronosync
diff --git a/tests/integrated-tests/test-socket.cpp b/tests/integrated-tests/test-socket.cpp
new file mode 100644
index 0000000..4b81554
--- /dev/null
+++ b/tests/integrated-tests/test-socket.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "socket.hpp"
+
+#include "boost-test.hpp"
+
+namespace chronosync {
+namespace test {
+
+using std::string;
+using std::vector;
+using std::map;
+
+/**
+ * @brief Emulate an app that use the Socket class
+ *
+ * The app has two types of data set: one is simply string while the other is integer array.
+ * For each type of data set, the app has a specific fetching strategy.
+ */
+class SocketTestApp : noncopyable
+{
+public:
+ SocketTestApp(const Name& syncPrefix,
+ const Name& userPrefix,
+ ndn::Face& face,
+ bool isNum)
+ : sum(0)
+ , socket(syncPrefix,
+ userPrefix,
+ face,
+ isNum ? bind(&SocketTestApp::fetchNumbers, this, _1) :
+ bind(&SocketTestApp::fetchAll, this, _1))
+ {
+ }
+
+ void
+ set(const shared_ptr<const Data>& dataPacket)
+ {
+ // std::cerr << "set Data" << std::endl;
+ Name dataName(dataPacket->getName());
+ string str2(reinterpret_cast<const char*>(dataPacket->getContent().value()),
+ dataPacket->getContent().value_size());
+ data.insert(make_pair(dataName, str2));
+ }
+
+ void
+ set(Name name, const char* buf, int len)
+ {
+ string str2(buf, len);
+ data.insert(make_pair(name, str2));
+ }
+
+ void
+ setNum(const shared_ptr<const Data>& dataPacket)
+ {
+ // std::cerr << "setNum Data" << std::endl;
+ size_t n = dataPacket->getContent().value_size() / 4;
+ const uint32_t* numbers = reinterpret_cast<const uint32_t*>(dataPacket->getContent().value());
+ for (size_t i = 0; i < n; i++) {
+ sum += numbers[i];
+ }
+ }
+
+ void
+ setNum(Name name, const uint8_t* buf, int len)
+ {
+ BOOST_ASSERT(len >= 4);
+
+ int n = len / 4;
+ const uint32_t* numbers = reinterpret_cast<const uint32_t*>(buf);
+ for (int i = 0; i < n; i++) {
+ sum += numbers[i];
+ }
+ }
+
+ void
+ fetchAll(const vector<MissingDataInfo>& v)
+ {
+ // std::cerr << "fetchAll" << std::endl;
+ for (int i = 0; i < v.size(); i++) {
+ for(SeqNo s = v[i].low; s <= v[i].high; ++s) {
+ socket.fetchData(v[i].session, s,
+ bind(&SocketTestApp::set, this, _1));
+ }
+ }
+ }
+
+ void
+ fetchNumbers(const vector<MissingDataInfo> &v)
+ {
+ // std::cerr << "fetchNumbers" << std::endl;
+ for (int i = 0; i < v.size(); i++) {
+ for(SeqNo s = v[i].low; s <= v[i].high; ++s) {
+ socket.fetchData(v[i].session, s,
+ bind(&SocketTestApp::setNum, this, _1));
+ }
+ }
+ }
+
+ string
+ toString()
+ {
+ string str = "\n";
+ for (map<Name, string>::iterator it = data.begin(); it != data.end(); ++it) {
+ str += "<";
+ str += it->first.toUri();
+ str += "|";
+ str += it->second;
+ str += ">";
+ str += "\n";
+ }
+
+ return str;
+ }
+
+ map<ndn::Name, string> data;
+ uint32_t sum;
+ Socket socket;
+};
+
+class SocketFixture
+{
+public:
+ SocketFixture()
+ : syncPrefix("/ndn/broadcast/sync")
+ , scheduler(io)
+ {
+ syncPrefix.appendVersion();
+ userPrefix[0] = Name("/user0");
+ userPrefix[1] = Name("/user1");
+ userPrefix[2] = Name("/user2");
+
+ faces[0] = make_shared<ndn::Face>(ref(io));
+ faces[1] = make_shared<ndn::Face>(ref(io));
+ faces[2] = make_shared<ndn::Face>(ref(io));
+ }
+
+ void
+ createSocket(size_t idx, bool isNum)
+ {
+ app[idx] = make_shared<SocketTestApp>(syncPrefix, userPrefix[idx], ref(*faces[idx]), isNum);
+ sessionName[idx] = app[idx]->socket.getLogic().getSessionName();
+ }
+
+ void
+ publishAppData(size_t idx, const string& data)
+ {
+ app[idx]->socket.publishData(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(),
+ ndn::time::milliseconds(1000));
+ }
+
+ void
+ setAppData(size_t idx, SeqNo seqNo, const string& data)
+ {
+ Name dataName = sessionName[idx];
+ dataName.appendNumber(seqNo);
+ app[idx]->set(dataName, data.c_str(), data.size());
+ }
+
+ void
+ publishAppNum(size_t idx, const uint8_t* buf, size_t size)
+ {
+ app[idx]->socket.publishData(buf, size, ndn::time::milliseconds(1000));
+ }
+
+ void
+ setAppNum(size_t idx, SeqNo seqNo, const uint8_t* buf, size_t size)
+ {
+ Name dataName = sessionName[idx];
+ dataName.appendNumber(seqNo);
+ app[idx]->setNum(dataName, buf, size);
+ }
+
+ void
+ check(int round)
+ {
+ BOOST_CHECK_EQUAL(app[0]->toString(), app[1]->toString());
+ BOOST_CHECK_EQUAL(app[0]->toString(), app[2]->toString());
+ }
+
+ void
+ check2Num(int num)
+ {
+ BOOST_CHECK_EQUAL(app[0]->sum, app[1]->sum);
+ BOOST_CHECK_EQUAL(app[1]->sum, num);
+ }
+
+ void
+ terminate()
+ {
+ io.stop();
+ }
+
+ Name syncPrefix;
+ Name userPrefix[3];
+ Name sessionName[3];
+
+ boost::asio::io_service io;
+ shared_ptr<ndn::Face> faces[3];
+ ndn::Scheduler scheduler;
+ shared_ptr<SocketTestApp> app[3];
+};
+
+
+
+BOOST_FIXTURE_TEST_SUITE(SocketTests, SocketFixture)
+
+BOOST_AUTO_TEST_CASE(BasicData)
+{
+ scheduler.scheduleEvent(ndn::time::milliseconds(0),
+ bind(&SocketFixture::createSocket, this, 0, false));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(50),
+ bind(&SocketFixture::createSocket, this, 1, false));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(100),
+ bind(&SocketFixture::createSocket, this, 2, false));
+
+ string data0 = "Very funny Scotty, now beam down my clothes";
+ scheduler.scheduleEvent(ndn::time::milliseconds(150),
+ bind(&SocketFixture::publishAppData, this, 0, data0));
+ scheduler.scheduleEvent(ndn::time::milliseconds(1150),
+ bind(&SocketFixture::setAppData, this, 0, 1, data0));
+ scheduler.scheduleEvent(ndn::time::milliseconds(1160),
+ bind(&SocketFixture::check, this, 1));
+
+ string data1 = "Yes, give me that ketchup";
+ string data2 = "Don't look conspicuous, it draws fire";
+ scheduler.scheduleEvent(ndn::time::milliseconds(1170),
+ bind(&SocketFixture::publishAppData, this, 0, data1));
+ scheduler.scheduleEvent(ndn::time::milliseconds(1180),
+ bind(&SocketFixture::publishAppData, this, 0, data2));
+ scheduler.scheduleEvent(ndn::time::milliseconds(2150),
+ bind(&SocketFixture::setAppData, this, 0, 2, data1));
+ scheduler.scheduleEvent(ndn::time::milliseconds(2160),
+ bind(&SocketFixture::setAppData, this, 0, 3, data2));
+ scheduler.scheduleEvent(ndn::time::milliseconds(2170),
+ bind(&SocketFixture::check, this, 2));
+
+ string data3 = "You surf the Internet, I surf the real world";
+ string data4 = "I got a fortune cookie once that said 'You like Chinese food'";
+ string data5 = "Real men wear pink. Why? Because their wives make them";
+ scheduler.scheduleEvent(ndn::time::milliseconds(3180),
+ bind(&SocketFixture::publishAppData, this, 2, data3));
+ scheduler.scheduleEvent(ndn::time::milliseconds(3200),
+ bind(&SocketFixture::publishAppData, this, 1, data4));
+ scheduler.scheduleEvent(ndn::time::milliseconds(3210),
+ bind(&SocketFixture::publishAppData, this, 1, data5));
+ scheduler.scheduleEvent(ndn::time::milliseconds(4710),
+ bind(&SocketFixture::setAppData, this, 2, 1, data3));
+ scheduler.scheduleEvent(ndn::time::milliseconds(4720),
+ bind(&SocketFixture::setAppData, this, 1, 1, data4));
+ scheduler.scheduleEvent(ndn::time::milliseconds(4730),
+ bind(&SocketFixture::setAppData, this, 1, 2, data5));
+ scheduler.scheduleEvent(ndn::time::milliseconds(4800),
+ bind(&SocketFixture::check, this, 3));
+
+ // not sure weither this is simultanous data generation from multiple sources
+ string data6 = "Shakespeare says: 'Prose before hos.'";
+ string data7 = "Pick good people, talent never wears out";
+ scheduler.scheduleEvent(ndn::time::milliseconds(5500),
+ bind(&SocketFixture::publishAppData, this, 0, data6));
+ scheduler.scheduleEvent(ndn::time::milliseconds(5500),
+ bind(&SocketFixture::publishAppData, this, 1, data7));
+ scheduler.scheduleEvent(ndn::time::milliseconds(6800),
+ bind(&SocketFixture::setAppData, this, 0, 4, data6));
+ scheduler.scheduleEvent(ndn::time::milliseconds(6800),
+ bind(&SocketFixture::setAppData, this, 1, 3, data7));
+ scheduler.scheduleEvent(ndn::time::milliseconds(6900),
+ bind(&SocketFixture::check, this, 4));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(7000),
+ bind(&SocketFixture::terminate, this));
+
+ io.run();
+}
+
+BOOST_AUTO_TEST_CASE(BasicNumber)
+{
+ scheduler.scheduleEvent(ndn::time::milliseconds(0),
+ bind(&SocketFixture::createSocket, this, 0, true));
+ scheduler.scheduleEvent(ndn::time::milliseconds(50),
+ bind(&SocketFixture::createSocket, this, 1, true));
+
+ uint32_t num1[5] = {0, 1, 2, 3, 4};
+ uint8_t* buf1 = reinterpret_cast<uint8_t*>(num1);
+ size_t size1 = sizeof(num1);
+ scheduler.scheduleEvent(ndn::time::milliseconds(100),
+ bind(&SocketFixture::publishAppNum, this, 0, buf1, size1));
+ scheduler.scheduleEvent(ndn::time::milliseconds(150),
+ bind(&SocketFixture::setAppNum, this, 0, 0, buf1, size1));
+ scheduler.scheduleEvent(ndn::time::milliseconds(1000),
+ bind(&SocketFixture::check2Num, this, 10));
+
+ uint32_t num2[5] = {9, 7, 2, 1, 1};
+ uint8_t* buf2 = reinterpret_cast<uint8_t*>(num2);
+ size_t size2 = sizeof(num2);
+ scheduler.scheduleEvent(ndn::time::milliseconds(1100),
+ bind(&SocketFixture::publishAppNum, this, 1, buf2, size2));
+ scheduler.scheduleEvent(ndn::time::milliseconds(1150),
+ bind(&SocketFixture::setAppNum, this, 1, 0, buf2, size2));
+ scheduler.scheduleEvent(ndn::time::milliseconds(2000),
+ bind(&SocketFixture::check2Num, this, 30));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(7000),
+ bind(&SocketFixture::terminate, this));
+
+ io.run();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace chronosync
diff --git a/tests/test-leaf.cpp.outdated b/tests/test-leaf.cpp.outdated
deleted file mode 100644
index bad1971..0000000
--- a/tests/test-leaf.cpp.outdated
+++ /dev/null
@@ -1,105 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/test/unit_test.hpp>
-#include <boost/test/output_test_stream.hpp>
-using boost::test_tools::output_test_stream;
-
-#include <boost/make_shared.hpp>
-
-#include "sync-full-leaf.h"
-#include "sync-diff-leaf.h"
-#include "sync-std-name-info.h"
-
-using namespace Sync;
-using namespace std;
-using namespace boost;
-
-BOOST_AUTO_TEST_SUITE(LeafTests)
-
-BOOST_AUTO_TEST_CASE (LeafBase)
-{
- NameInfoConstPtr name = StdNameInfo::FindOrCreate ("/test/name");
- BOOST_CHECK (name != 0);
-
- // find the same name
- BOOST_CHECK (name.get () == StdNameInfo::FindOrCreate ("/test/name").get ());
- BOOST_CHECK_EQUAL (name.use_count (), 1);
-
- BOOST_CHECK_NO_THROW (DiffLeaf x (name, SeqNo (12)));
- BOOST_CHECK_EQUAL (name.use_count (), 1);
-
- BOOST_CHECK_NO_THROW (DiffLeaf x (name));
- BOOST_CHECK_EQUAL (name.use_count (), 1);
-
- DiffLeaf updateLeaf (name, SeqNo (12));
- BOOST_CHECK_EQUAL (name.use_count (), 2);
-
- DiffLeaf removeLeaf (name);
- BOOST_CHECK_EQUAL (name.use_count (), 3);
-
- BOOST_CHECK_EQUAL (updateLeaf.getOperation (), UPDATE);
- BOOST_CHECK_EQUAL (updateLeaf.getSeq ().getSession (), 0);
- BOOST_CHECK_EQUAL (updateLeaf.getSeq ().getSeq (), 12);
-
- BOOST_CHECK_EQUAL (removeLeaf.getOperation (), REMOVE);
- BOOST_CHECK_EQUAL (removeLeaf.getSeq ().getSession (), 0);
- BOOST_CHECK_EQUAL (removeLeaf.getSeq ().getSeq (), 0);
-
- BOOST_REQUIRE_NO_THROW (FullLeaf x (name, SeqNo (12)));
- FullLeaf fullLeaf (name, SeqNo (12));
- BOOST_CHECK_EQUAL (name.use_count (), 4);
-}
-
-BOOST_AUTO_TEST_CASE (LeafDigest)
-{
- BOOST_CHECK_EQUAL (StdNameInfo::FindOrCreate ("/test/name").use_count (), 1);
- NameInfoConstPtr name = StdNameInfo::FindOrCreate ("/test/name");
- FullLeaf fullLeaf (name, SeqNo (12));
-
- // fullLeafDigest = hash ( hash(name), hash (session, seqNo) )
-
- // Digest manualDigest;
-
- // Digest manualNameDigest;
- // manualNameDigest << "/test/name";
- // manualNameDigest.finalize ();
-
- // Digest manualSeqNoDigest;
- // manualSeqNoDigest << 0 << 13;
- // manualSeqNoDigest.finalize ();
-
- // manualDigest << manualNameDigest << manualSeqNoDigest;
- // manualDigest.finalize ();
-
- // cout << manualDigest << "\n\n";
-
- output_test_stream output;
- output << fullLeaf.getDigest ();
- // BOOST_CHECK (output.is_equal ("991f8cf6262dfe0f519c63f6e9b92fe69e741a9b", true)); // for sha1
- BOOST_CHECK (output.is_equal ("526d63e6e1f05f97502fd500a1729c4907f3841483ae4561b7e6307c40188f35", true)); // for sha256
-
- fullLeaf.setSeq (SeqNo (13));
- output << fullLeaf.getDigest ();
- BOOST_CHECK (!output.is_equal ("991f8cf6262dfe0f519c63f6e9b92fe69e741a9b", false));
- // BOOST_CHECK (output.is_equal ("585a8687ab41d5c29f86e5906c8f188ddca816b3", true)); // for sha1
- BOOST_CHECK (output.is_equal ("39fefe65b3e1021776c07d3a9a3023c6c7cdf12724ee7f3a98b813b22f46d5ec", true)); // for sha256
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit-tests/test-data-fetch-and-publish.cpp.outdated b/tests/unit-tests/test-data-fetch-and-publish.cpp.outdated
deleted file mode 100644
index 583717c..0000000
--- a/tests/unit-tests/test-data-fetch-and-publish.cpp.outdated
+++ /dev/null
@@ -1,108 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
-#include <boost/test/unit_test.hpp>
-#include <boost/test/output_test_stream.hpp>
-#include <map>
-using boost::test_tools::output_test_stream;
-
-#include <boost/make_shared.hpp>
-
-#include "sync-ccnx-wrapper.h"
-#include "sync-app-data-fetch.h"
-#include "sync-app-data-publish.h"
-
-using namespace Sync;
-using namespace std;
-using namespace boost;
-
-class TestStructApp {
-public:
- map<string, string> data;
- void set(string str1, string str2) {
- data.insert(make_pair(str1, str2));
- }
-
- void erase(string str1, string str2) {
- data.erase(str1);
- }
-
- string toString(){
- map<string, string>::iterator it = data.begin();
- string str = "";
- for (; it != data.end(); ++it){
- str += "<";
- str += it->first;
- str += "|";
- str += it->second;
- str += ">";
- }
- return str;
- }
-
-};
-
-BOOST_AUTO_TEST_CASE (AppDataPublishAndFetchTest)
-{
- TestStructApp foo;
- TestStructApp bar;
-
- string interest = "/april/fool";
- string seq[5] = {"0", "1", "2", "3", "4" };
- string str[5] = {"panda", "express", "tastes", "so", "good"};
-
- for (int i = 0; i < 5; i++) {
- foo.set(interest + "/" + "0/" + seq[i], str[i]);
- }
-
- boost::function<void (string, string)> setFunc =
- bind(&TestStructApp::set, &bar, _1, _2);
-
- shared_ptr<CcnxWrapper> handle(new CcnxWrapper());
-
- AppDataFetch fetcher(handle, setFunc);
- AppDataPublish publisher(handle);
-
- for (int i = 1; i <= 5; i++) {
- publisher.publishData(interest, 0, str[i - 1], 5);
- }
-
- BOOST_CHECK_EQUAL(publisher.getNextSeq(interest, 0), 5);
- BOOST_CHECK_EQUAL(publisher.getRecentData(interest, 0), str[4]);
-
- fetcher.onUpdate (interest, SeqNo (0,4), SeqNo (0,-1));
- // give time for ccnd to react
- sleep(1);
- BOOST_CHECK_EQUAL(foo.toString(), bar.toString());
-
-
- boost::function<void (string, string)> eraseFunc =
- bind(&TestStructApp::erase, &bar, _1, _2);
- fetcher.setDataCallback(eraseFunc);
-
- fetcher.onUpdate (interest, SeqNo (0,4), SeqNo (0,-1));
- // give time for ccnd to react
- sleep(1);
- TestStructApp poo;
-
- BOOST_CHECK_EQUAL(poo.toString(), bar.toString());
-
-}
-*/
diff --git a/tests/unit-tests/test-diff-state.cpp b/tests/unit-tests/test-diff-state.cpp
new file mode 100644
index 0000000..a4b740f
--- /dev/null
+++ b/tests/unit-tests/test-diff-state.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "diff-state.hpp"
+#include "diff-state-container.hpp"
+#include "boost-test.hpp"
+
+namespace chronosync {
+namespace test {
+
+BOOST_AUTO_TEST_SUITE(DiffStateTests)
+
+BOOST_AUTO_TEST_CASE(Diff)
+{
+ State state;
+
+ Name info1("/test/name");
+ info1.appendNumber(0);
+
+ Name info2("/test/name");
+ info2.appendNumber(1);
+
+ Name info3("/test/name");
+ info3.appendNumber(2);
+
+ DiffStatePtr root = make_shared<DiffState>();
+ DiffStatePtr action1 = make_shared<DiffState>();
+ root->setNext(action1);
+
+ state.update(info1, 1);
+ action1->update(info1, 1);
+ action1->setRootDigest(state.getRootDigest());
+
+ DiffStatePtr action2 = make_shared<DiffState>();
+ action1->setNext(action2);
+
+ state.update(info2, 1);
+ state.update(info3, 2);
+ action2->update(info2, 1);
+ action2->update(info3, 2);
+ action2->setRootDigest(state.getRootDigest());
+
+ LeafContainer::index<ordered>::type::iterator it;
+ ConstStatePtr diff0 = root->diff();
+ BOOST_CHECK_EQUAL(diff0->getLeaves().size(), 3);
+ it = diff0->getLeaves().get<ordered>().begin();
+ BOOST_CHECK_EQUAL((*it)->getSessionName(), info1);
+ BOOST_CHECK_EQUAL((*it)->getSeq(), 1);
+ it++;
+ BOOST_CHECK_EQUAL((*it)->getSessionName(), info2);
+ BOOST_CHECK_EQUAL((*it)->getSeq(), 1);
+ it++;
+ BOOST_CHECK_EQUAL((*it)->getSessionName(), info3);
+ BOOST_CHECK_EQUAL((*it)->getSeq(), 2);
+
+ ConstStatePtr diff1 = action1->diff();
+ BOOST_CHECK_EQUAL(diff1->getLeaves().size(), 2);
+ it = diff1->getLeaves().get<ordered>().begin();
+ BOOST_CHECK_EQUAL((*it)->getSessionName(), info2);
+ BOOST_CHECK_EQUAL((*it)->getSeq(), 1);
+ it++;
+ BOOST_CHECK_EQUAL((*it)->getSessionName(), info3);
+ BOOST_CHECK_EQUAL((*it)->getSeq(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Container)
+{
+ DiffStateContainer container;
+
+ State state;
+
+ Name info1("/test/name");
+ info1.appendNumber(0);
+
+ Name info2("/test/name");
+ info2.appendNumber(1);
+
+ Name info3("/test/name");
+ info3.appendNumber(2);
+
+ DiffStatePtr root = make_shared<DiffState>();
+ DiffStatePtr action1 = make_shared<DiffState>();
+ root->setNext(action1);
+
+ state.update(info1, 1);
+ action1->update(info1, 1);
+ ndn::ConstBufferPtr digest1 = state.getRootDigest();
+ action1->setRootDigest(digest1);
+
+ DiffStatePtr action2 = make_shared<DiffState>();
+ action1->setNext(action2);
+
+ state.update(info2, 1);
+ state.update(info3, 2);
+ action2->update(info2, 1);
+ action2->update(info3, 2);
+ ndn::ConstBufferPtr digest2 = state.getRootDigest();
+ action2->setRootDigest(digest2);
+
+ DiffStatePtr action3 = make_shared<DiffState>();
+ state.update(info1, 3);
+ state.update(info3, 4);
+ action3->update(info1, 3);
+ action3->update(info3, 4);
+ ndn::ConstBufferPtr digest3 = state.getRootDigest();
+ action3->setRootDigest(digest3);
+
+ container.insert(action3);
+ container.insert(action2);
+ container.insert(action1);
+ BOOST_CHECK_EQUAL(container.size(), 3);
+
+ DiffStateContainer::index<sequenced>::type::iterator it = container.get<sequenced>().begin();
+ BOOST_CHECK(*(*it)->getRootDigest() == *digest3);
+ it++;
+ BOOST_CHECK(*(*it)->getRootDigest() == *digest2);
+ it++;
+ BOOST_CHECK(*(*it)->getRootDigest() == *digest1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace chronosync
diff --git a/tests/unit-tests/test-digest.cpp.outdated b/tests/unit-tests/test-digest.cpp.outdated
deleted file mode 100644
index cbc5ced..0000000
--- a/tests/unit-tests/test-digest.cpp.outdated
+++ /dev/null
@@ -1,101 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/test/unit_test.hpp>
-#include <boost/test/output_test_stream.hpp>
-using boost::test_tools::output_test_stream;
-
-#include "sync-digest.h"
-#include <iostream>
-#include <sstream>
-
-using namespace Sync;
-using namespace Sync::Error;
-using namespace std;
-using namespace boost;
-
-BOOST_AUTO_TEST_SUITE(DigestTests)
-
-BOOST_AUTO_TEST_CASE (BasicTest)
-{
- Digest d0;
- BOOST_REQUIRE (d0.empty ());
-}
-
-BOOST_AUTO_TEST_CASE (DigestGenerationTest)
-{
- Digest d1;
- BOOST_CHECK_NO_THROW (d1 << "1\n");
-
- // without explicit finalizing, Digest will not be complete and printing out will cause assert
- BOOST_CHECK (d1.empty ());
-
- // fix hash
- d1.finalize ();
-
- BOOST_CHECK_NO_THROW (d1.getHash ());
- BOOST_CHECK (!d1.empty ());
- BOOST_CHECK (d1 == d1);
-
- BOOST_CHECK_THROW (d1 << "2", DigestCalculationError);
-
- output_test_stream output;
- BOOST_CHECK_NO_THROW (output << d1);
- // BOOST_CHECK (output.check_length (40, false) );
- // BOOST_CHECK (output.is_equal ("e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e", true)); // for sha1
- BOOST_CHECK (output.check_length (64, false) );
- BOOST_CHECK (output.is_equal ("4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865", true)); // for sha256
-}
-
-BOOST_AUTO_TEST_CASE (DigestComparison)
-{
- Digest d1;
- BOOST_CHECK_NO_THROW (d1 << "1\n");
- // BOOST_CHECK_THROW (d1 == d1, DigestCalculationError);
- BOOST_CHECK_NO_THROW (d1.finalize ());
- BOOST_CHECK (d1 == d1);
-
- Digest d2;
- BOOST_CHECK_NO_THROW (d2 << "2\n");
- BOOST_CHECK_NO_THROW (d2.finalize ());
- BOOST_CHECK (d1 != d2);
-
- Digest d3;
- // istringstream is (string ("e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e")); // real sha-1 for "1\n"
- istringstream is (string ("4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865")); // real sha256 for "1\n"
- BOOST_CHECK_NO_THROW (is >> d3);
- BOOST_CHECK (!d3.empty ());
- BOOST_CHECK (d3 == d1);
- BOOST_CHECK (d3 != d2);
-
- istringstream is2 (string ("25fa44f2b31c1fb553b6021e7360d07d5d91ff5e")); // some fake hash
- BOOST_CHECK_THROW (is2 >> d3, DigestCalculationError); // >> can be used only once
-
- Digest d4;
- BOOST_CHECK_THROW (is2 >> d4, DigestCalculationError); // is2 is now empty. empty >> is not allowed
-
- istringstream is3 (string ("25fa44f2b31c1fb553b6021e7360d07d5d91ff5e")); // some fake hash
- BOOST_CHECK_NO_THROW (is3 >> d4);
-
- BOOST_CHECK (d4 != d1);
- BOOST_CHECK (d4 != d2);
- BOOST_CHECK (d4 != d3);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit-tests/test-interest-table.cpp b/tests/unit-tests/test-interest-table.cpp
new file mode 100644
index 0000000..6f9e97a
--- /dev/null
+++ b/tests/unit-tests/test-interest-table.cpp
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "interest-table.hpp"
+
+#include <ndn-cxx/util/scheduler.hpp>
+#include <unistd.h>
+
+#include "boost-test.hpp"
+
+namespace chronosync {
+namespace test {
+
+class InterestTableFixture
+{
+public:
+ InterestTableFixture()
+ : scheduler(io)
+ {
+ uint8_t origin[4] = {0x01, 0x02, 0x03, 0x04};
+ Name prefix("/test/prefix");
+
+ Name interestName1;
+ digest1 = ndn::crypto::sha256(origin, 1);
+ interestName1.append(prefix).append(name::Component(digest1));
+ interest1 = make_shared<Interest>(interestName1);
+ interest1->setInterestLifetime(time::milliseconds(100));
+
+ Name interestName2;
+ digest2 = ndn::crypto::sha256(origin, 2);
+ interestName2.append(prefix).append(name::Component(digest2));
+ interest2 = make_shared<Interest>(interestName2);
+ interest2->setInterestLifetime(time::milliseconds(100));
+
+ Name interestName3;
+ digest3 = ndn::crypto::sha256(origin, 3);
+ interestName3.append(prefix).append(name::Component(digest3));
+ interest3 = make_shared<Interest>(interestName3);
+ interest3->setInterestLifetime(time::milliseconds(100));
+ }
+
+ void
+ insert(InterestTable& table,
+ shared_ptr<Interest> interest,
+ ndn::ConstBufferPtr digest)
+ {
+ table.insert(interest, digest);
+ }
+
+ void
+ check(InterestTable& table, size_t size)
+ {
+ BOOST_CHECK_EQUAL(table.size(), size);
+ }
+
+ void
+ terminate()
+ {
+ io.stop();
+ }
+
+ shared_ptr<Interest> interest1;
+ ndn::ConstBufferPtr digest1;
+
+ shared_ptr<Interest> interest2;
+ ndn::ConstBufferPtr digest2;
+
+ shared_ptr<Interest> interest3;
+ ndn::ConstBufferPtr digest3;
+
+ boost::asio::io_service io;
+ ndn::Scheduler scheduler;
+};
+
+BOOST_FIXTURE_TEST_SUITE(InterestTableTest, InterestTableFixture)
+
+BOOST_AUTO_TEST_CASE(Container)
+{
+ InterestContainer container;
+
+ container.insert(make_shared<UnsatisfiedInterest>(interest1, digest1));
+ container.insert(make_shared<UnsatisfiedInterest>(interest2, digest2));
+ container.insert(make_shared<UnsatisfiedInterest>(interest3, digest3));
+
+ BOOST_CHECK_EQUAL(container.size(), 3);
+ BOOST_CHECK(container.find(digest3) != container.end());
+ BOOST_CHECK(container.find(digest2) != container.end());
+ BOOST_CHECK(container.find(digest1) != container.end());
+}
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ InterestTable table(io);
+
+ table.insert(interest1, digest1);
+ table.insert(interest2, digest2);
+ table.insert(interest3, digest3);
+
+ BOOST_CHECK_EQUAL(table.size(), 3);
+ InterestTable::const_iterator it = table.begin();
+ BOOST_CHECK(it != table.end());
+ it++;
+ BOOST_CHECK(it != table.end());
+ it++;
+ BOOST_CHECK(it != table.end());
+ it++;
+ BOOST_CHECK(it == table.end());
+
+ BOOST_CHECK_EQUAL(table.size(), 3);
+ table.erase(digest1);
+ BOOST_CHECK_EQUAL(table.size(), 2);
+ table.erase(digest2);
+ BOOST_CHECK_EQUAL(table.size(), 1);
+ ConstUnsatisfiedInterestPtr pendingInterest = *table.begin();
+ table.clear();
+ BOOST_CHECK_EQUAL(table.size(), 0);
+ BOOST_CHECK(*pendingInterest->digest == *digest3);
+}
+
+BOOST_AUTO_TEST_CASE(Expire)
+{
+ InterestTable table(io);
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(50),
+ ndn::bind(&InterestTableFixture::insert, this,
+ ndn::ref(table), interest1, digest1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(150),
+ ndn::bind(&InterestTableFixture::insert, this,
+ ndn::ref(table), interest2, digest2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(150),
+ ndn::bind(&InterestTableFixture::insert, this,
+ ndn::ref(table), interest3, digest3));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(200),
+ ndn::bind(&InterestTableFixture::insert, this,
+ ndn::ref(table), interest2, digest2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(220),
+ ndn::bind(&InterestTableFixture::check, this,
+ ndn::ref(table), 2));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(270),
+ ndn::bind(&InterestTableFixture::check, this,
+ ndn::ref(table), 1));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(420),
+ ndn::bind(&InterestTableFixture::check, this,
+ ndn::ref(table), 0));
+
+ scheduler.scheduleEvent(ndn::time::milliseconds(500),
+ ndn::bind(&InterestTableFixture::terminate, this));
+
+ io.run();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace chronosync
diff --git a/tests/unit-tests/test-interest-table.cpp.outdated b/tests/unit-tests/test-interest-table.cpp.outdated
deleted file mode 100644
index 2db3eed..0000000
--- a/tests/unit-tests/test-interest-table.cpp.outdated
+++ /dev/null
@@ -1,42 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/test/unit_test.hpp>
-#include <boost/test/output_test_stream.hpp>
-#include <map>
-using boost::test_tools::output_test_stream;
-
-#include <boost/make_shared.hpp>
-
-#include "sync-interest-table.h"
-
-using namespace Sync;
-using namespace std;
-using namespace boost;
-
-BOOST_AUTO_TEST_CASE (InterestTableTest)
-{
- // Alex: test is broken due to changes in SyncInterestTable
- // cerr << "InterestTableTest is broken" << endl;
-
- // SyncInterestTable *table = 0;
- // BOOST_CHECK_NO_THROW (table = new SyncInterestTable ());
-
- // BOOST_CHECK_NO_THROW (delete table);
-}
diff --git a/tests/unit-tests/test-leaf.cpp b/tests/unit-tests/test-leaf.cpp
new file mode 100644
index 0000000..5553a05
--- /dev/null
+++ b/tests/unit-tests/test-leaf.cpp
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "leaf.hpp"
+#include "leaf-container.hpp"
+#include <ndn-cxx/encoding/buffer-stream.hpp>
+
+#include "boost-test.hpp"
+
+
+namespace chronosync {
+namespace test {
+
+BOOST_AUTO_TEST_SUITE(LeafTests)
+
+BOOST_AUTO_TEST_CASE(LeafBasic)
+{
+ Name userPrefix("/test/name");
+ BOOST_CHECK_NO_THROW(Leaf leaf(userPrefix, 1, 10));
+
+ Leaf leaf(userPrefix, 1, 10);
+ Name sessionName = userPrefix;
+ sessionName.appendNumber(1);
+ BOOST_CHECK_EQUAL(leaf.getSessionName(), sessionName);
+ BOOST_CHECK_EQUAL(leaf.getSeq(), 10);
+
+ leaf.setSeq(9);
+ BOOST_CHECK_EQUAL(leaf.getSeq(), 10);
+ leaf.setSeq(11);
+ BOOST_CHECK_EQUAL(leaf.getSeq(), 11);
+}
+
+BOOST_AUTO_TEST_CASE(LeafDigest)
+{
+ using namespace CryptoPP;
+
+ std::string hexResult = "05fe7f728d3341e9eff82526277b02171044124d0a52e8c4610982261c20de2b";
+ ndn::OBufferStream os;
+ StringSource(hexResult, true, new HexDecoder(new FileSink(os)));
+ ndn::ConstBufferPtr result = os.buf();
+
+ Name userPrefix("/test/name");
+ Leaf leaf(userPrefix, 1, 10);
+
+ BOOST_CHECK_NO_THROW(leaf.getDigest());
+
+ ndn::ConstBufferPtr digest = leaf.getDigest();
+ BOOST_CHECK(*result == *digest);
+}
+
+BOOST_AUTO_TEST_CASE(Container)
+{
+ LeafPtr leaf1 = make_shared<Leaf>(Name("/test/name"), 1, 10);
+ LeafPtr leaf2 = make_shared<Leaf>(Name("/test/name"), 2, 10);
+
+ LeafContainer container;
+
+ container.insert(leaf1);
+ container.insert(leaf2);
+
+ Name idx1("/test/name");
+ idx1.appendNumber(1);
+
+ Name idx2("/test/name");
+ idx2.appendNumber(2);
+
+ Name idx3("/test/name");
+ idx3.appendNumber(3);
+
+ Name idx4("/test/mane");
+ idx4.appendNumber(4);
+
+ LeafContainer::index<hashed>::type& hashedIndex = container.get<hashed>();
+
+ BOOST_CHECK_EQUAL(container.get<ordered>().size(), 2);
+ BOOST_CHECK_EQUAL(container.get<hashed>().size(), 2);
+ BOOST_CHECK(container.find(idx1) != container.end());
+ BOOST_CHECK(container.find(idx2) != container.end());
+ BOOST_CHECK(container.find(idx3) == container.end());
+
+ BOOST_CHECK(hashedIndex.find(idx1) != hashedIndex.end());
+ BOOST_CHECK(hashedIndex.find(idx2) != hashedIndex.end());
+ BOOST_CHECK(hashedIndex.find(idx3) == hashedIndex.end());
+
+ LeafPtr leaf3 = make_shared<Leaf>(Name("/test/mane"), 3, 10);
+ container.insert(leaf3);
+
+ Name idx0("/test/mane");
+ idx0.appendNumber(3);
+
+ LeafContainer::index<ordered>::type::iterator it = container.get<ordered>().begin();
+ BOOST_CHECK_EQUAL((*it)->getSessionName(), idx0);
+ it++;
+ BOOST_REQUIRE(it != container.get<ordered>().end());
+ BOOST_CHECK_EQUAL((*it)->getSessionName(), idx1);
+ it++;
+ BOOST_REQUIRE(it != container.get<ordered>().end());
+ BOOST_CHECK_EQUAL((*it)->getSessionName(), idx2);
+
+
+ BOOST_CHECK(hashedIndex.find(idx0) != hashedIndex.end());
+ BOOST_CHECK(hashedIndex.find(idx1) != hashedIndex.end());
+ BOOST_CHECK(hashedIndex.find(idx2) != hashedIndex.end());
+ BOOST_CHECK(hashedIndex.find(idx4) == hashedIndex.end());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace chronosync
diff --git a/tests/unit-tests/test-pit.cpp.outdated b/tests/unit-tests/test-pit.cpp.outdated
deleted file mode 100644
index 39b36b9..0000000
--- a/tests/unit-tests/test-pit.cpp.outdated
+++ /dev/null
@@ -1,72 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/test/unit_test.hpp>
-#include <boost/test/output_test_stream.hpp>
-using boost::test_tools::output_test_stream;
-#include <vector>
-
-#include <boost/make_shared.hpp>
-
-#include "sync-interest-table.h"
-#include "sync-logging.h"
-
-using namespace Sync;
-using namespace std;
-using namespace boost;
-
-// string sitToString(SyncInterestTable *sit) {
-// vector<string> ent = sit->fetchAll();
-// sort(ent.begin(), ent.end());
-// string str = "";
-// while(!ent.empty()){
-// str += ent.back();
-// ent.pop_back();
-// }
-// return str;
-// }
-
-BOOST_AUTO_TEST_CASE (SyncInterestTableTest)
-{
- cerr << "SyncInterestTableTest is broken" << endl;
-
- // INIT_LOGGERS ();
- // INIT_LOGGER ("Test.Pit");
-
- // SyncInterestTable sit;
- // sit.insert("/ucla.edu/0");
- // sit.insert("/ucla.edu/1");
- // string str = sitToString(&sit);
- // BOOST_CHECK_EQUAL(str, "/ucla.edu/1/ucla.edu/0");
-
- // str = sitToString(&sit);
- // BOOST_CHECK_EQUAL(str, "");
-
- // _LOG_DEBUG ("Adding 0 and 1");
- // sit.insert("/ucla.edu/0");
- // sit.insert("/ucla.edu/1");
- // sleep(2);
- // _LOG_DEBUG ("Adding 0 and 2");
- // sit.insert("/ucla.edu/0");
- // sit.insert("/ucla.edu/2");
- // sleep(3);
- // _LOG_DEBUG ("Checking");
- // str = sitToString(&sit);
- // BOOST_CHECK_EQUAL(str, "/ucla.edu/2/ucla.edu/0");
-}
diff --git a/tests/unit-tests/test-scheduler.cc.tmp b/tests/unit-tests/test-scheduler.cc.tmp
deleted file mode 100644
index 2a53a22..0000000
--- a/tests/unit-tests/test-scheduler.cc.tmp
+++ /dev/null
@@ -1,175 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/test/unit_test.hpp>
-#include <boost/test/output_test_stream.hpp>
-#include <map>
-using boost::test_tools::output_test_stream;
-
-#include <boost/make_shared.hpp>
-#include "sync-scheduler.h"
-#include "sync-logic.h"
-
-using namespace Sync;
-using namespace std;
-using namespace boost;
-
-
-
-// void funcUpdate (const std::string &, const SeqNo &newSeq, const SeqNo &oldSeq)
-// {
-// cout << "funcUpdate\n";
-// }
-
-// void funcRemove (const std::string &)
-// {
-// cout << "funcRemove\n";
-// }
-
-enum SCHEDULE_LABELS
- {
- TEST_LABEL,
- ANOTHER_LABEL
- };
-
-struct SchedulerFixture
-{
- SchedulerFixture ()
- : counter (0)
- {}
-
- int counter;
-
- Scheduler *scheduler;
-
- void everySecond ()
- {
- // cout << "." << flush;
- counter ++;
-
- if (counter < 9)
- scheduler->schedule (boost::posix_time::milliseconds (100),
- boost::bind (&SchedulerFixture::everySecond, this),
- TEST_LABEL);
- }
-
- void setCounterFive ()
- {
- counter = 5;
- }
-
- void setCounterThree ()
- {
- counter = 3;
- }
-};
-
-
-#ifdef _DEBUG
-
-BOOST_FIXTURE_TEST_SUITE (SchedulerTestSuite, SchedulerFixture)
-
-BOOST_AUTO_TEST_CASE (BasicTest)
-{
- BOOST_CHECK_NO_THROW (scheduler = new Scheduler ());
-
- scheduler->schedule (posix_time::milliseconds (100),
- bind (&SchedulerFixture::everySecond, this),
- TEST_LABEL);
-
- sleep (1);
- // cout << counter << endl;
- BOOST_CHECK_EQUAL (counter, 9); // generally, should be 9
-
- scheduler->schedule (posix_time::seconds (2),
- bind (&SchedulerFixture::setCounterFive, this),
- TEST_LABEL);
-
- this_thread::sleep (posix_time::milliseconds (400)); // just in case
-
- scheduler->schedule (posix_time::milliseconds (600),
- bind (&SchedulerFixture::setCounterThree, this),
- TEST_LABEL);
-
- this_thread::sleep (posix_time::milliseconds (500));
- BOOST_CHECK_EQUAL (counter, 9); // still 9
-
- this_thread::sleep (posix_time::milliseconds (200));
- BOOST_CHECK_EQUAL (counter, 3);
-
- this_thread::sleep (posix_time::milliseconds (1000));
- BOOST_CHECK_EQUAL (counter, 5);
-
- scheduler->schedule (posix_time::milliseconds (100),
- bind (&SchedulerFixture::setCounterThree, this),
- ANOTHER_LABEL);
- this_thread::sleep (posix_time::milliseconds (50));
- scheduler->cancel (ANOTHER_LABEL);
- this_thread::sleep (posix_time::milliseconds (150));
- BOOST_CHECK_EQUAL (counter, 5);
-
- BOOST_CHECK_NO_THROW (delete scheduler);
-}
-
-BOOST_AUTO_TEST_SUITE_END ()
-
-
-void funcUpdate( const std::string &/*prefix*/, const SeqNo &/*newSeq*/, const SeqNo &/*oldSeq*/ )
-{
-}
-
-void funcPass( const std::vector<MissingDataInfo> &v)
-{
-}
-
-void funcRemove( const std::string &/*prefix*/ )
-{
-}
-
-BOOST_AUTO_TEST_CASE (SyncLogicSchedulerTest)
-{
- SyncLogic *logic = 0;
- BOOST_CHECK_NO_THROW (logic = new SyncLogic ("/prefix", funcPass, funcRemove));
- this_thread::sleep (posix_time::milliseconds (100));
-
- Scheduler &scheduler = logic->getScheduler ();
- BOOST_CHECK_EQUAL (scheduler.getEventsSize (), 1);
-
- BOOST_CHECK_NO_THROW (logic->respondSyncInterest ("/prefix/e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e"));
- BOOST_CHECK_EQUAL (scheduler.getEventsSize (), 2);
-
- this_thread::sleep (posix_time::milliseconds (100)); // max waiting time
- BOOST_CHECK_EQUAL (scheduler.getEventsSize (), 1);
-
- BOOST_CHECK_NO_THROW (logic->respondSyncInterest ("/prefix/e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e"));
- BOOST_CHECK_NO_THROW (logic->respondSyncInterest ("/prefix/e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e"));
- BOOST_CHECK_NO_THROW (logic->respondSyncInterest ("/prefix/e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e"));
- BOOST_CHECK_NO_THROW (logic->respondSyncInterest ("/prefix/e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e"));
- BOOST_CHECK_EQUAL (scheduler.getEventsSize (), 5);
-
- this_thread::sleep (posix_time::milliseconds (19)); // min waiting time is 20
- BOOST_CHECK_EQUAL (scheduler.getEventsSize (), 5);
-
- this_thread::sleep (posix_time::milliseconds (100)); // max waiting time
- BOOST_CHECK_EQUAL (scheduler.getEventsSize (), 1);
-
- BOOST_CHECK_NO_THROW (delete logic);
-}
-
-#endif
diff --git a/tests/unit-tests/test-socket.cpp.outdated b/tests/unit-tests/test-socket.cpp.outdated
deleted file mode 100644
index d04ded9..0000000
--- a/tests/unit-tests/test-socket.cpp.outdated
+++ /dev/null
@@ -1,619 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/test/unit_test.hpp>
-#include <boost/test/output_test_stream.hpp>
-using boost::test_tools::output_test_stream;
-
-#include <boost/make_shared.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-#include "sync-logging.h"
-#include "sync-socket.h"
-#include "sync-validator.h"
-#include <ndn-cxx/security/validator-null.hpp>
-
-extern "C" {
-#include <unistd.h>
-}
-
-using namespace Sync;
-using namespace std;
-using namespace boost;
-
-INIT_LOGGER ("Test.AppSocket");
-
-#define PRINT
-// std::cout << "Line: " << __LINE__ << std::endl;
-
-class TestSocketApp {
-public:
- TestSocketApp()
- : sum(0)
- {}
-
- map<ndn::Name, string> data;
- void set(const ndn::shared_ptr<const ndn::Data>& dataPacket) {
- // _LOG_FUNCTION (this << ", " << str1);
- ndn::Name dataName(dataPacket->getName());
- string str2(reinterpret_cast<const char*>(dataPacket->getContent().value()), dataPacket->getContent().value_size());
- data.insert(make_pair(dataName, str2));
- // cout << str1 << ", " << str2 << endl;
- }
-
- void set(ndn::Name name, const char * buf, int len) {
- string str2(buf, len);
- data.insert(make_pair(name, str2));
- }
-
- void setNum(const ndn::shared_ptr<const ndn::Data>& data) {
- int n = data->getContent().value_size() / 4;
- uint32_t *numbers = new uint32_t [n];
- memcpy(numbers, data->getContent().value(), data->getContent().value_size());
- for (int i = 0; i < n; i++) {
- sum += numbers[i];
- }
- delete numbers;
-
- }
-
- void setNum(ndn::Name name, const char * buf, int len) {
- int n = len / 4;
- int *numbers = new int [n];
- memcpy(numbers, buf, len);
- for (int i = 0; i < n; i++) {
- sum += numbers[i];
- }
- delete numbers;
- }
-
- uint32_t sum;
-
- void fetchAll(const vector<MissingDataInfo> &v, SyncSocket *socket) {
- int n = v.size();
-
- PRINT
-
- for (int i = 0; i < n; i++) {
- for(SeqNo s = v[i].low; s <= v[i].high; ++s) {
- //PRINT
- socket->fetchData(v[i].prefix, s, bind(&TestSocketApp::set, this, _1));
- }
- }
- }
-
- void fetchNumbers(const vector<MissingDataInfo> &v, SyncSocket *socket) {
- int n = v.size();
-
- PRINT
-
- // std::cout << "In fetchNumbers. size of v is: " << n << std::endl;
- for (int i = 0; i < n; i++) {
- // std::cout << "In fetchNumbers. v[i].low is (" <<v[i].low.getSession() <<", " << v[i].low.getSeq() << ") v[i].high is ("<<v[i].high.getSession() <<", " <<v[i].high.getSeq()<<")" << std::endl;
- for(SeqNo s = v[i].low; s <= v[i].high; ++s) {
- PRINT
- socket->fetchData(v[i].prefix, s, bind(&TestSocketApp::setNum, this, _1));
- }
- }
- }
-
- void pass(const string &prefix) {
- }
-
- string toString(){
- map<ndn::Name, string>::iterator it = data.begin();
- string str = "\n";
- for (; it != data.end(); ++it){
- str += "<";
- str += it->first.toUri();
- str += "|";
- str += it->second;
- str += ">";
- str += "\n";
- }
-
- return str;
- }
-
-};
-
-class TestSet1{
-public:
- TestSet1(ndn::shared_ptr<boost::asio::io_service> ioService)
- : m_face1(new ndn::Face(*ioService))
- , m_face2(new ndn::Face(*ioService))
- , m_face3(new ndn::Face(*ioService))
- , m_name1("/irl.cs.ucla.edu/" + boost::lexical_cast<std::string>(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count()))
- , m_name2("/yakshi.org/" + boost::lexical_cast<std::string>(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count()))
- , m_name3("/google.com/" + boost::lexical_cast<std::string>(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count()))
- {
- m_id1 = m_keyChain.getCertificate(m_keyChain.createIdentity(m_name1));
- m_id2 = m_keyChain.getCertificate(m_keyChain.createIdentity(m_name2));
- m_id3 = m_keyChain.getCertificate(m_keyChain.createIdentity(m_name3));
-
- m_rule = ndn::make_shared<ndn::SecRuleRelative>("^(<>*)<><>$",
- "^(<>*)<><KEY><ksk-.*><ID-CERT>$",
- "==", "\\1", "\\1", true);
- }
-
- void
- createSyncSocket1()
- {
- _LOG_DEBUG ("s1");
-
- m_s1 = ndn::shared_ptr<SyncSocket>
- (new SyncSocket("/let/us/sync",
- "/irl.cs.ucla.edu",
- 0,
- false,
- "/",
- m_face1,
- *m_id1,
- m_rule,
- bind(&TestSocketApp::fetchAll, &m_a1, _1, _2),
- bind(&TestSocketApp::pass, &m_a1, _1)));
-
- m_s1->addParticipant(*m_id2);
- }
-
- void
- createSyncSocket2()
- {
- _LOG_DEBUG ("s2");
-
- m_s2 = ndn::shared_ptr<SyncSocket>
- (new SyncSocket("/let/us/sync",
- "/yakshi.org",
- 0,
- false,
- "/",
- m_face2,
- *m_id2,
- m_rule,
- bind(&TestSocketApp::fetchAll, &m_a2, _1, _2),
- bind(&TestSocketApp::pass, &m_a2, _1)));
-
- m_s2->addParticipant(*m_id1);
- m_s2->addParticipant(*m_id3);
- }
-
- void
- createSyncSocket3()
- {
- _LOG_DEBUG ("s3");
-
- m_s3 = ndn::shared_ptr<SyncSocket>
- (new SyncSocket("/let/us/sync",
- "/google.com",
- 0,
- false,
- "/",
- m_face3,
- *m_id3,
- m_rule,
- bind(&TestSocketApp::fetchAll, &m_a3, _1, _2),
- bind(&TestSocketApp::pass, &m_a3, _1)));
-
- m_s3->addParticipant(*m_id2);
- }
-
- void
- publishSocket1(string data)
- {
- _LOG_DEBUG ("s1 publish");
- m_s1->publishData (reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), 1000);
- }
-
- void
- publishSocket2(string data)
- {
- _LOG_DEBUG ("s2 publish");
- m_s2->publishData (reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), 1000);
- }
-
- void
- publishSocket3(string data)
- {
- _LOG_DEBUG ("s3 publish");
- m_s3->publishData (reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), 1000);
- }
-
- void
- setSocket1(string suffix, string data)
- {
- _LOG_DEBUG ("a1 set");
- ndn::Name name("/irl.cs.ucla.edu");
- name.append(suffix);
- m_a1.set (name, data.c_str(), data.size());
- }
-
- void
- setSocket2(string suffix, string data)
- {
- _LOG_DEBUG ("a2 set");
- ndn::Name name("/yakshi.org");
- name.append(suffix);
- m_a2.set (name, data.c_str(), data.size());
- }
-
- void
- setSocket3(string suffix, string data)
- {
- _LOG_DEBUG ("a3 set");
- ndn::Name name("/google.com");
- name.append(suffix);
- m_a3.set (name, data.c_str(), data.size());
- }
-
- void
- check(int round)
- {
- BOOST_CHECK_EQUAL(m_a1.toString(), m_a2.toString());
- BOOST_CHECK_EQUAL(m_a2.toString(), m_a3.toString());
- }
-
- void
- done(ndn::shared_ptr<boost::asio::io_service> ioService)
- {
- m_s1.reset();
- m_s2.reset();
- m_s3.reset();
-
- m_keyChain.deleteIdentity(m_name1);
- m_keyChain.deleteIdentity(m_name2);
- m_keyChain.deleteIdentity(m_name3);
-
- ioService->stop();
- }
-
- ndn::KeyChain m_keyChain;
- ndn::shared_ptr<ndn::SecRuleRelative> m_rule;
-
- ndn::shared_ptr<ndn::Face> m_face1, m_face2, m_face3;
- ndn::Name m_name1, m_name2, m_name3;
- TestSocketApp m_a1, m_a2, m_a3;
- ndn::shared_ptr<ndn::IdentityCertificate> m_id1, m_id2, m_id3;
- ndn::shared_ptr<SyncSocket> m_s1, m_s2, m_s3;
-};
-
-class TestSet2{
-public:
- TestSet2(ndn::shared_ptr<boost::asio::io_service> ioService)
- : m_face1(new ndn::Face(*ioService))
- , m_face2(new ndn::Face(*ioService))
- , m_name1("/xiaonei.com/" + boost::lexical_cast<std::string>(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count()))
- , m_name2("/mitbbs.com/" + boost::lexical_cast<std::string>(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count()))
- {
- m_id1 = m_keyChain.getCertificate(m_keyChain.createIdentity(m_name1));
- m_id2 = m_keyChain.getCertificate(m_keyChain.createIdentity(m_name2));
-
- m_rule = ndn::make_shared<ndn::SecRuleRelative>("^(<>*)<><>$",
- "^(<>*)<><KEY><ksk-.*><ID-CERT>$",
- "==", "\\1", "\\1", true);
- }
-
- void
- createSyncSocket1()
- {
- _LOG_DEBUG ("s1");
-
- m_s1 = ndn::shared_ptr<SyncSocket>
- (new SyncSocket("/this/is/the/prefix",
- "/xiaonei.com",
- 0,
- false,
- "/",
- m_face1,
- *m_id1,
- m_rule,
- bind(&TestSocketApp::fetchNumbers, &m_a1, _1, _2),
- bind(&TestSocketApp::pass, &m_a1, _1)));
-
- m_s1->addParticipant(*m_id2);
- }
-
- void
- createSyncSocket2()
- {
- _LOG_DEBUG ("s2");
-
- m_s2 = ndn::shared_ptr<SyncSocket>
- (new SyncSocket("/this/is/the/prefix",
- "/mitbbs.com",
- 0,
- false,
- "/",
- m_face2,
- *m_id2,
- m_rule,
- bind(&TestSocketApp::fetchNumbers, &m_a2, _1, _2),
- bind(&TestSocketApp::pass, &m_a2, _1)));
-
- m_s2->addParticipant(*m_id1);
- }
-
- void
- publishSocket1(string data)
- {
- _LOG_DEBUG ("s1 publish");
- m_s1->publishData (reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), 1000);
- }
-
- void
- publishSocket2(string data)
- {
- _LOG_DEBUG ("s2 publish");
- m_s2->publishData (reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), 1000);
- }
-
- void
- setSocket1(const char* ptr, size_t size)
- {
- _LOG_DEBUG ("a1 setNum");
- m_a1.setNum ("/xiaonei.com", ptr, size);
- }
-
- void
- setSocket2(const char* ptr, size_t size)
- {
- _LOG_DEBUG ("a2 setNum");
- m_a2.setNum ("/mitbbs.com", ptr, size);
- }
-
- void
- check(int num)
- {
- _LOG_DEBUG ("codnum " << num);
- _LOG_DEBUG ("a1 sum " << m_a1.sum);
- _LOG_DEBUG ("a2 sum " << m_a2.sum);
-
- BOOST_CHECK(m_a1.sum == m_a2.sum && m_a1.sum == num);
- }
-
- void
- done(ndn::shared_ptr<boost::asio::io_service> ioService)
- {
- m_s1.reset();
- m_s2.reset();
-
- m_keyChain.deleteIdentity(m_name1);
- m_keyChain.deleteIdentity(m_name2);
-
- ioService->stop();
- }
-
- ndn::KeyChain m_keyChain;
- ndn::shared_ptr<ndn::SecRuleRelative> m_rule;
-
- TestSocketApp m_a1, m_a2;
- ndn::shared_ptr<ndn::IdentityCertificate> m_id1, m_id2;
- ndn::shared_ptr<ndn::Face> m_face1, m_face2;
- ndn::Name m_name1, m_name2;
- ndn::shared_ptr<SyncSocket> m_s1, m_s2;
-};
-
-
-
-class TestSet3{
-public:
- TestSet3(ndn::shared_ptr<boost::asio::io_service> ioService)
- : m_face1(new ndn::Face(*ioService))
- , m_face2(new ndn::Face(*ioService))
- , m_name1("/xiaonei.com/" + boost::lexical_cast<std::string>(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count()))
- , m_name2("/mitbbs.com/" + boost::lexical_cast<std::string>(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count()))
- {
- m_id1 = m_keyChain.getCertificate(m_keyChain.createIdentity(m_name1));
- m_id2 = m_keyChain.getCertificate(m_keyChain.createIdentity(m_name2));
-
- m_rule = ndn::make_shared<ndn::SecRuleRelative>("^(<>*)<><>$",
- "^(<>*)<><KEY><ksk-.*><ID-CERT>$",
- "==", "\\1", "\\1", true);
- }
-
- void
- createSyncSocket1()
- {
- _LOG_DEBUG ("s1");
-
- m_s1 = ndn::shared_ptr<SyncSocket>
- (new SyncSocket("/this/is/the/prefix",
- "/xiaonei.com",
- 1,
- true,
- "/abc",
- m_face1,
- *m_id1,
- m_rule,
- bind(&TestSocketApp::fetchNumbers, &m_a1, _1, _2),
- bind(&TestSocketApp::pass, &m_a1, _1)));
-
- m_s1->addParticipant(*m_id2);
- }
-
- void
- createSyncSocket2()
- {
- _LOG_DEBUG ("s2");
-
- m_s2 = ndn::shared_ptr<SyncSocket>
- (new SyncSocket("/this/is/the/prefix",
- "/mitbbs.com",
- 1,
- false,
- "/",
- m_face2,
- *m_id2,
- m_rule,
- bind(&TestSocketApp::fetchNumbers, &m_a2, _1, _2),
- bind(&TestSocketApp::pass, &m_a2, _1)));
-
- m_s2->addParticipant(*m_id1);
- }
-
- void
- publishSocket1(string data)
- {
- _LOG_DEBUG ("s1 publish");
- m_s1->publishData (reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), 1000);
- }
-
- void
- publishSocket2(string data)
- {
- _LOG_DEBUG ("s2 publish");
- m_s2->publishData (reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), 1000);
- }
-
- void
- setSocket1(const char* ptr, size_t size)
- {
- _LOG_DEBUG ("a1 setNum");
- m_a1.setNum ("/xiaonei.com", ptr, size);
- }
-
- void
- setSocket2(const char* ptr, size_t size)
- {
- _LOG_DEBUG ("a2 setNum");
- m_a2.setNum ("/mitbbs.com", ptr, size);
- }
-
- void
- check(int num)
- {
- _LOG_DEBUG ("codnum " << num);
- _LOG_DEBUG ("a1 sum " << m_a1.sum);
- _LOG_DEBUG ("a2 sum " << m_a2.sum);
-
- BOOST_CHECK(m_a1.sum == m_a2.sum && m_a1.sum == num);
- }
-
- void
- done(ndn::shared_ptr<boost::asio::io_service> ioService)
- {
- m_s1.reset();
- m_s2.reset();
-
- m_keyChain.deleteIdentity(m_name1);
- m_keyChain.deleteIdentity(m_name2);
-
- ioService->stop();
- }
-
- ndn::KeyChain m_keyChain;
- ndn::shared_ptr<ndn::SecRuleRelative> m_rule;
-
- TestSocketApp m_a1, m_a2;
- ndn::shared_ptr<ndn::IdentityCertificate> m_id1, m_id2;
- ndn::shared_ptr<ndn::Face> m_face1, m_face2;
- ndn::Name m_name1, m_name2;
- ndn::shared_ptr<SyncSocket> m_s1, m_s2;
-};
-
-BOOST_AUTO_TEST_CASE (AppSocketTest1)
-{
- INIT_LOGGERS ();
-
- ndn::shared_ptr<boost::asio::io_service> ioService = ndn::make_shared<boost::asio::io_service>();
- ndn::Scheduler scheduler(*ioService);
- TestSet1 testSet1(ioService);
-
- scheduler.scheduleEvent(ndn::time::milliseconds(0), ndn::bind(&TestSet1::createSyncSocket1, &testSet1));
- scheduler.scheduleEvent(ndn::time::milliseconds(50), ndn::bind(&TestSet1::createSyncSocket2, &testSet1));
- scheduler.scheduleEvent(ndn::time::milliseconds(100), ndn::bind(&TestSet1::createSyncSocket3, &testSet1));
- string data0 = "Very funny Scotty, now beam down my clothes";
- scheduler.scheduleEvent(ndn::time::milliseconds(150), ndn::bind(&TestSet1::publishSocket1, &testSet1, data0));
- scheduler.scheduleEvent(ndn::time::milliseconds(1150), ndn::bind(&TestSet1::setSocket1, &testSet1, "/0/1", data0));
- scheduler.scheduleEvent(ndn::time::milliseconds(1160), ndn::bind(&TestSet1::check, &testSet1, 1));
- string data1 = "Yes, give me that ketchup";
- string data2 = "Don't look conspicuous, it draws fire";
- scheduler.scheduleEvent(ndn::time::milliseconds(1170), ndn::bind(&TestSet1::publishSocket1, &testSet1, data1));
- scheduler.scheduleEvent(ndn::time::milliseconds(1180), ndn::bind(&TestSet1::publishSocket1, &testSet1, data2));
- scheduler.scheduleEvent(ndn::time::milliseconds(2150), ndn::bind(&TestSet1::setSocket1, &testSet1, "/0/2", data1));
- scheduler.scheduleEvent(ndn::time::milliseconds(2160), ndn::bind(&TestSet1::setSocket1, &testSet1, "/0/3", data2));
- scheduler.scheduleEvent(ndn::time::milliseconds(2170), ndn::bind(&TestSet1::check, &testSet1, 2));
- string data3 = "You surf the Internet, I surf the real world";
- string data4 = "I got a fortune cookie once that said 'You like Chinese food'";
- string data5 = "Real men wear pink. Why? Because their wives make them";
- scheduler.scheduleEvent(ndn::time::milliseconds(3180), ndn::bind(&TestSet1::publishSocket3, &testSet1, data3));
- scheduler.scheduleEvent(ndn::time::milliseconds(3200), ndn::bind(&TestSet1::publishSocket2, &testSet1, data4));
- scheduler.scheduleEvent(ndn::time::milliseconds(3210), ndn::bind(&TestSet1::publishSocket2, &testSet1, data5));
- scheduler.scheduleEvent(ndn::time::milliseconds(4710), ndn::bind(&TestSet1::setSocket3, &testSet1, "/0/1", data3));
- scheduler.scheduleEvent(ndn::time::milliseconds(4720), ndn::bind(&TestSet1::setSocket2, &testSet1, "/0/2", data4));
- scheduler.scheduleEvent(ndn::time::milliseconds(4730), ndn::bind(&TestSet1::setSocket2, &testSet1, "/0/3", data5));
- scheduler.scheduleEvent(ndn::time::milliseconds(4800), ndn::bind(&TestSet1::check, &testSet1, 3));
- // not sure weither this is simultanous data generation from multiple sources
- _LOG_DEBUG ("Simultaneous publishing");
- string data6 = "Shakespeare says: 'Prose before hos.'";
- string data7 = "Pick good people, talent never wears out";
- scheduler.scheduleEvent(ndn::time::milliseconds(5500), ndn::bind(&TestSet1::publishSocket1, &testSet1, data6));
- scheduler.scheduleEvent(ndn::time::milliseconds(5500), ndn::bind(&TestSet1::publishSocket2, &testSet1, data7));
- scheduler.scheduleEvent(ndn::time::milliseconds(6800), ndn::bind(&TestSet1::setSocket1, &testSet1, "/0/4", data6));
- scheduler.scheduleEvent(ndn::time::milliseconds(6800), ndn::bind(&TestSet1::setSocket2, &testSet1, "/0/4", data7));
- scheduler.scheduleEvent(ndn::time::milliseconds(6900), ndn::bind(&TestSet1::check, &testSet1, 4));
- scheduler.scheduleEvent(ndn::time::milliseconds(7000), ndn::bind(&TestSet1::done, &testSet1, ioService));
-
- ioService->run();
-}
-
-BOOST_AUTO_TEST_CASE (AppSocketTest2)
-{
- ndn::shared_ptr<boost::asio::io_service> ioService = ndn::make_shared<boost::asio::io_service>();
- ndn::Scheduler scheduler(*ioService);
- TestSet2 testSet2(ioService);
-
- scheduler.scheduleEvent(ndn::time::milliseconds(0), ndn::bind(&TestSet2::createSyncSocket1, &testSet2));
- scheduler.scheduleEvent(ndn::time::milliseconds(50), ndn::bind(&TestSet2::createSyncSocket2, &testSet2));
- uint32_t num[5] = {0, 1, 2, 3, 4};
- string data0((const char *) num, sizeof(num));
- scheduler.scheduleEvent(ndn::time::milliseconds(100), ndn::bind(&TestSet2::publishSocket1, &testSet2, data0));
- scheduler.scheduleEvent(ndn::time::milliseconds(150), ndn::bind(&TestSet2::setSocket1, &testSet2, (const char *) num, sizeof (num)));
- scheduler.scheduleEvent(ndn::time::milliseconds(1000), ndn::bind(&TestSet2::check, &testSet2, 10));
- uint32_t newNum[5] = {9, 7, 2, 1, 1};
- string data1((const char *) newNum, sizeof(newNum));
- scheduler.scheduleEvent(ndn::time::milliseconds(1100), ndn::bind(&TestSet2::publishSocket2, &testSet2, data1));
- scheduler.scheduleEvent(ndn::time::milliseconds(1150), ndn::bind(&TestSet2::setSocket2, &testSet2, (const char *) newNum, sizeof (newNum)));
- scheduler.scheduleEvent(ndn::time::milliseconds(2000), ndn::bind(&TestSet2::check, &testSet2, 30));
- scheduler.scheduleEvent(ndn::time::milliseconds(7000), ndn::bind(&TestSet2::done, &testSet2, ioService));
-
- ioService->run();
-}
-
-BOOST_AUTO_TEST_CASE (AppSocketTest3)
-{
- ndn::shared_ptr<boost::asio::io_service> ioService = ndn::make_shared<boost::asio::io_service>();
- ndn::Scheduler scheduler(*ioService);
- TestSet3 testSet3(ioService);
-
- scheduler.scheduleEvent(ndn::time::milliseconds(0), ndn::bind(&TestSet3::createSyncSocket1, &testSet3));
- scheduler.scheduleEvent(ndn::time::milliseconds(200), ndn::bind(&TestSet3::createSyncSocket2, &testSet3));
- uint32_t num[5] = {0, 1, 2, 3, 4};
- string data0((const char *) num, sizeof(num));
- scheduler.scheduleEvent(ndn::time::milliseconds(1000), ndn::bind(&TestSet3::publishSocket1, &testSet3, data0));
- scheduler.scheduleEvent(ndn::time::milliseconds(1500), ndn::bind(&TestSet3::setSocket1, &testSet3, (const char *) num, sizeof (num)));
- scheduler.scheduleEvent(ndn::time::milliseconds(2000), ndn::bind(&TestSet3::check, &testSet3, 10));
- uint32_t newNum[5] = {9, 7, 2, 1, 1};
- string data1((const char *) newNum, sizeof(newNum));
- scheduler.scheduleEvent(ndn::time::milliseconds(3000), ndn::bind(&TestSet3::publishSocket2, &testSet3, data1));
- scheduler.scheduleEvent(ndn::time::milliseconds(3500), ndn::bind(&TestSet3::setSocket2, &testSet3, (const char *) newNum, sizeof (newNum)));
- scheduler.scheduleEvent(ndn::time::milliseconds(5000), ndn::bind(&TestSet3::check, &testSet3, 30));
- scheduler.scheduleEvent(ndn::time::milliseconds(7000), ndn::bind(&TestSet3::done, &testSet3, ioService));
-
- ioService->run();
-}
diff --git a/tests/unit-tests/test-state.cc.outdated b/tests/unit-tests/test-state.cc.outdated
deleted file mode 100644
index 433f8d9..0000000
--- a/tests/unit-tests/test-state.cc.outdated
+++ /dev/null
@@ -1,291 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#define BOOST_TEST_DYN_LINK 1
-#define BOOST_TEST_NO_MAIN 1
-// #define BOOST_TEST_MODULE StateTests
-#include <boost/test/unit_test.hpp>
-#include <boost/test/output_test_stream.hpp>
-using boost::test_tools::output_test_stream;
-
-#include <boost/make_shared.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-#include "sync-std-name-info.h"
-#include "sync-full-state.h"
-#include "sync-diff-state.h"
-
-using namespace Sync;
-using namespace std;
-using namespace boost;
-
-BOOST_AUTO_TEST_SUITE(StateTests)
-
-BOOST_AUTO_TEST_CASE (FullStateTest)
-{
- BOOST_CHECK_NO_THROW (FullState ());
- FullState state;
- BOOST_CHECK_EQUAL (state.getLeaves ().size (), 0);
-
- output_test_stream output;
- output << state.getTimeFromLastUpdate ();
- BOOST_CHECK (output.is_equal ("not-a-date-time", true));
-
- NameInfoConstPtr name = StdNameInfo::FindOrCreate ("/test/name");
- BOOST_CHECK_NO_THROW (state.update (name, SeqNo (12)));
- BOOST_CHECK_NO_THROW (state.update (name, SeqNo (12)));
- BOOST_CHECK_NO_THROW (state.update (name, SeqNo (12)));
- BOOST_CHECK_EQUAL (state.getLeaves ().size (), 1);
- BOOST_CHECK_EQUAL ((*state.getLeaves ().begin ())->getSeq ().getSeq (), 12);
-
- BOOST_CHECK_NO_THROW (state.update (name, SeqNo (13)));
- BOOST_CHECK_EQUAL ((*state.getLeaves ().begin ())->getSeq ().getSeq (), 13);
-
- BOOST_CHECK_NO_THROW (state.remove (name));
- BOOST_CHECK_EQUAL (state.getLeaves ().size (), 0);
-
- BOOST_CHECK_EQUAL (state.getTimeFromLastUpdate ().total_milliseconds (), 0);
-}
-
-BOOST_AUTO_TEST_CASE (DiffStateTest)
-{
- BOOST_CHECK_NO_THROW (DiffState ());
- DiffState state;
- BOOST_CHECK_EQUAL (state.getLeaves ().size (), 0);
-
- NameInfoConstPtr name = StdNameInfo::FindOrCreate ("/test/name");
- BOOST_CHECK_NO_THROW (state.update (name, SeqNo (12)));
- BOOST_CHECK_NO_THROW (state.update (name, SeqNo (12)));
- BOOST_CHECK_NO_THROW (state.update (name, SeqNo (12)));
- BOOST_CHECK_EQUAL (state.getLeaves ().size (), 1);
- BOOST_CHECK_EQUAL ((*state.getLeaves ().begin ())->getSeq ().getSeq (), 12);
-
- BOOST_CHECK_NO_THROW (state.update (name, SeqNo (13)));
- BOOST_CHECK_EQUAL ((*state.getLeaves ().begin ())->getSeq ().getSeq (), 13);
-
- BOOST_CHECK_NO_THROW (state.remove (name));
- BOOST_CHECK_EQUAL (state.getLeaves ().size (), 1);
- BOOST_CHECK_EQUAL ((*state.getLeaves ().begin ())->getSeq ().getSeq (), 0);
-}
-
-BOOST_AUTO_TEST_CASE (FullStateDigestTest)
-{
- FullState state;
- BOOST_CHECK_EQUAL (state.getLeaves ().size (), 0);
-
- NameInfoConstPtr name3 = StdNameInfo::FindOrCreate ("3");
- NameInfoConstPtr name2 = StdNameInfo::FindOrCreate ("2");
- NameInfoConstPtr name1 = StdNameInfo::FindOrCreate ("1");
-
- state.update (name1, SeqNo (10));
- DigestConstPtr digest1 = state.getDigest ();
-
- state.update (name2, SeqNo (12));
- DigestConstPtr digest2 = state.getDigest ();
-
- BOOST_CHECK (digest1.get () != digest2.get ());
- BOOST_CHECK (!digest1->empty ());
- BOOST_CHECK (!digest2->empty ());
-
- state.update (name3, SeqNo (8));
- DigestConstPtr digest3 = state.getDigest ();
-
- BOOST_CHECK (digest1.get () != digest2.get ());
- BOOST_CHECK (digest2.get () != digest3.get ());
- BOOST_CHECK (digest1.get () != digest3.get ());
-
- BOOST_CHECK (*digest1 != *digest2);
- BOOST_CHECK (*digest2 != *digest3);
- BOOST_CHECK (*digest1 != *digest3);
-
- // removing elements. Digest should get reverted to digest1
- state.remove (name2);
- state.remove (name3);
- DigestConstPtr digest4 = state.getDigest ();
- BOOST_CHECK (*digest1 == *digest4);
-
- name2.reset (); // force destructor
- name3.reset (); // force destructor
- name3 = StdNameInfo::FindOrCreate ("3"); // this will enforce different (larger) hashing ID of name
- name2 = StdNameInfo::FindOrCreate ("2"); // this will enforce different (larger) hashing ID of name
-
- // adding in different order
- state.update (name3, SeqNo (8));
- state.update (name2, SeqNo (12));
- DigestConstPtr digest5 = state.getDigest ();
- BOOST_CHECK (*digest5 == *digest3);
-}
-
-BOOST_AUTO_TEST_CASE (FullStateXml)
-{
- FullState state;
-
- NameInfoConstPtr name3 = StdNameInfo::FindOrCreate ("3");
- NameInfoConstPtr name2 = StdNameInfo::FindOrCreate ("2");
- NameInfoConstPtr name1 = StdNameInfo::FindOrCreate ("1");
-
- state.update (name1, SeqNo (10));
- state.update (name2, SeqNo (12));
- state.update (name3, SeqNo (8));
-
- string xml1 = "<state>"
- "<item><name>1</name><seq><session>0</session><seqno>10</seqno></seq></item>"
- "<item><name>2</name><seq><session>0</session><seqno>12</seqno></seq></item>"
- "<item><name>3</name><seq><session>0</session><seqno>8</seqno></seq></item>"
- "</state>";
- {
- ostringstream os;
- os << state;
- string s = os.str ();
- // cout << s << endl;
- erase_all (s, "\n");
- BOOST_CHECK_EQUAL (s, xml1);
- }
-
- state.remove (name2);
- string xml2 = "<state>"
- "<item><name>1</name><seq><session>0</session><seqno>10</seqno></seq></item>"
- "<item><name>3</name><seq><session>0</session><seqno>8</seqno></seq></item>"
- "</state>";
- {
- ostringstream os;
- os << state;
- string s = os.str ();
- erase_all (s, "\n");
- BOOST_CHECK_EQUAL (s, xml2);
- }
-
- FullState state2;
- istringstream xml1_is (xml1);
- BOOST_CHECK_NO_THROW (xml1_is >> state2);
- {
- ostringstream os;
- os << state2;
- string xml1_test = os.str ();
- erase_all (xml1_test, "\n");
- BOOST_CHECK_EQUAL (xml1_test, xml1);
- }
-
- istringstream xml2_is ("<state><item action=\"remove\"><name>2</name></item></state>");
- BOOST_CHECK_NO_THROW (xml2_is >> state2);
-
- {
- ostringstream os;
- os << state2;
- string xml2_test = os.str ();
- erase_all (xml2_test, "\n");
- BOOST_CHECK_EQUAL (xml2_test, xml2);
- }
-}
-
-BOOST_AUTO_TEST_CASE (DiffStateXml)
-{
- DiffState state;
-
- NameInfoConstPtr name3 = StdNameInfo::FindOrCreate ("3");
- NameInfoConstPtr name2 = StdNameInfo::FindOrCreate ("2");
- NameInfoConstPtr name1 = StdNameInfo::FindOrCreate ("1");
-
- state.update (name1, SeqNo (10));
- state.update (name2, SeqNo (12));
- state.update (name3, SeqNo (8));
-
- string xml1 = "<state>"
- "<item action=\"update\"><name>1</name><seq><session>0</session><seqno>10</seqno></seq></item>"
- "<item action=\"update\"><name>2</name><seq><session>0</session><seqno>12</seqno></seq></item>"
- "<item action=\"update\"><name>3</name><seq><session>0</session><seqno>8</seqno></seq></item>"
- "</state>";
- {
- ostringstream os;
- os << state;
- string xml1_test = os.str ();
- erase_all (xml1_test, "\n");
- BOOST_CHECK_EQUAL (xml1_test, xml1);
- }
-
- state.remove (name2);
- string xml2 = "<state>"
- "<item action=\"update\"><name>1</name><seq><session>0</session><seqno>10</seqno></seq></item>"
- "<item action=\"remove\"><name>2</name></item>"
- "<item action=\"update\"><name>3</name><seq><session>0</session><seqno>8</seqno></seq></item>"
- "</state>";
- {
- ostringstream os;
- os << state;
- string xml2_test = os.str ();
- erase_all (xml2_test, "\n");
- BOOST_CHECK_EQUAL (xml2_test, xml2);
- }
-
- //////////// //////////// //////////// //////////// //////////// ////////////
-
- DiffState state2;
- istringstream xml1_is (xml1);
- BOOST_CHECK_NO_THROW (xml1_is >> state2);
-
- {
- ostringstream os;
- os << state2;
- string xml1_test = os.str ();
- erase_all (xml1_test, "\n");
- BOOST_CHECK_EQUAL (xml1_test, xml1);
- }
-
- istringstream xml2_is ("<state><item action=\"remove\"><name>2</name></item></state>");
- BOOST_CHECK_NO_THROW (xml2_is >> state2);
-
- {
- ostringstream os;
- os << state2;
- string xml2_test = os.str ();
- erase_all (xml2_test, "\n");
- BOOST_CHECK_EQUAL (xml2_test, xml2);
- }
-
-}
-
-BOOST_AUTO_TEST_CASE (DiffStateDiffTest)
-{
- DiffStatePtr root = make_shared<DiffState> ();
-
- DiffStatePtr head = make_shared<DiffState> ();
- root->setNext (head);
-
- head->update (StdNameInfo::FindOrCreate ("3"), SeqNo (1));
- head->remove (StdNameInfo::FindOrCreate ("1"));
-
- DiffStatePtr tail = make_shared<DiffState> ();
- head->setNext (tail);
-
- tail->update (StdNameInfo::FindOrCreate ("3"), SeqNo (2));
-
- {
- ostringstream os;
- os << *root->diff ();
- string diffState = os.str ();
- erase_all (diffState, "\n");
- BOOST_CHECK_EQUAL (diffState,
- "<state>"
- "<item action=\"remove\"><name>1</name></item>"
- "<item action=\"update\"><name>3</name><seq><session>0</session><seqno>2</seqno></seq></item>"
- "</state>");
- }
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit-tests/test-state.cpp b/tests/unit-tests/test-state.cpp
new file mode 100644
index 0000000..ed88373
--- /dev/null
+++ b/tests/unit-tests/test-state.cpp
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can 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.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "state.hpp"
+#include "boost-test.hpp"
+
+namespace chronosync {
+namespace test {
+
+using boost::tuple;
+
+BOOST_AUTO_TEST_SUITE(StateTests)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ BOOST_CHECK_NO_THROW(State());
+ State state;
+ BOOST_CHECK_EQUAL(state.getLeaves().size(), 0);
+
+ Name info("/test/name");
+ info.appendNumber(0);
+
+ BOOST_CHECK_NO_THROW(state.update(info, 12));
+
+ BOOST_CHECK_NO_THROW(state.reset());
+ BOOST_CHECK_EQUAL(state.getLeaves().size(), 0);
+
+ tuple<bool, bool, SeqNo> result;
+ result = state.update(info, 12);
+ BOOST_CHECK_EQUAL(result.get<0>(), true);
+ BOOST_CHECK_EQUAL(result.get<1>(), false);
+ BOOST_CHECK_EQUAL(result.get<2>(), 0);
+
+ BOOST_CHECK_NO_THROW(state.update(info, 12));
+ result = state.update(info, 12);
+ BOOST_CHECK_EQUAL(result.get<0>(), false);
+ BOOST_CHECK_EQUAL(result.get<1>(), false);
+ BOOST_CHECK_EQUAL(result.get<2>(), 0);
+
+ BOOST_CHECK_NO_THROW(state.update(info, 11));
+ result = state.update(info, 11);
+ BOOST_CHECK_EQUAL(result.get<0>(), false);
+ BOOST_CHECK_EQUAL(result.get<1>(), false);
+ BOOST_CHECK_EQUAL(result.get<2>(), 0);
+
+ BOOST_CHECK_EQUAL(state.getLeaves().size(), 1);
+ BOOST_CHECK_EQUAL((*state.getLeaves().begin())->getSeq(), 12);
+
+ BOOST_CHECK_NO_THROW(state.update(info, 13));
+ BOOST_CHECK_EQUAL(state.getLeaves().size(), 1);
+ BOOST_CHECK_EQUAL((*state.getLeaves().begin())->getSeq(), 13);
+
+ result = state.update(info, 14);
+ BOOST_CHECK_EQUAL(result.get<0>(), false);
+ BOOST_CHECK_EQUAL(result.get<1>(), true);
+ BOOST_CHECK_EQUAL(result.get<2>(), 13);
+
+ BOOST_CHECK_EQUAL(state.getLeaves().size(), 1);
+ BOOST_CHECK_EQUAL((*state.getLeaves().begin())->getSeq(), 14);
+
+ Name info2("/test/name");
+ info2.appendNumber(1);
+ BOOST_CHECK_NO_THROW(state.update(info2, 3));
+ BOOST_CHECK_EQUAL(state.getLeaves().size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(StateDigest)
+{
+ State state;
+ BOOST_CHECK_EQUAL(state.getLeaves().size(), 0);
+
+ Name info1("/test/name");
+ info1.appendNumber(0);
+
+ Name info2("/test/name");
+ info2.appendNumber(1);
+
+ Name info3("/test/mane");
+ info3.appendNumber(0);
+
+ state.update(info1, 10);
+ ndn::ConstBufferPtr digest1 = state.getRootDigest();
+
+ state.update(info2, 12);
+ ndn::ConstBufferPtr digest2 = state.getRootDigest();
+
+ state.update(info3, 8);
+ ndn::ConstBufferPtr digest3 = state.getRootDigest();
+
+ BOOST_CHECK(*digest1 != *digest2);
+ BOOST_CHECK(*digest2 != *digest3);
+ BOOST_CHECK(*digest1 != *digest3);
+
+ state.reset();
+
+ state.update(info1, 10);
+ ndn::ConstBufferPtr digest4 = state.getRootDigest();
+
+ state.update(info3, 8);
+ ndn::ConstBufferPtr digest5 = state.getRootDigest();
+
+ state.update(info2, 12);
+ ndn::ConstBufferPtr digest6 = state.getRootDigest();
+
+ BOOST_CHECK(*digest4 == *digest1);
+ BOOST_CHECK(*digest5 != *digest2);
+ BOOST_CHECK(*digest6 == *digest3);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeEncode)
+{
+ const uint8_t wire[] = {
+ 0x80, 0x2c, // SyncReply
+ 0x81, 0x14, // StateLeaf
+ 0x07, 0x0f, // Name: /test/name/[0]
+ 0x08, 0x04,
+ 0x74, 0x65, 0x73, 0x74,
+ 0x08, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65,
+ 0x08, 0x01,
+ 0x00,
+ 0x82, 0x1, // SeqNo: 14
+ 0x0e,
+ 0x81, 0x14, // StateLeaf
+ 0x07, 0x0f, // Name: /test/name/[1]
+ 0x08, 0x04,
+ 0x74, 0x65, 0x73, 0x74,
+ 0x08, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65,
+ 0x08, 0x01,
+ 0x01,
+ 0x82, 0x1, // SeqNo: 4
+ 0x04
+ };
+
+ Block block(wire, sizeof(wire));
+ State state;
+ BOOST_REQUIRE_NO_THROW(state.wireDecode(block));
+
+ BOOST_CHECK_EQUAL(state.getLeaves().size(), 2);
+ LeafContainer::index<ordered>::type::iterator it = state.getLeaves().get<ordered>().begin();
+ BOOST_CHECK_EQUAL((*it)->getSeq(), 14);
+ it++;
+ BOOST_CHECK_EQUAL((*it)->getSeq(), 4);
+
+
+ State state2;
+
+ Name info1("/test/name");
+ info1.appendNumber(0);
+ state2.update(info1, 14);
+
+ Name info2("/test/name");
+ info2.appendNumber(1);
+ state2.update(info2, 4);
+
+ BOOST_REQUIRE_NO_THROW(state2.wireEncode());
+ Block block2 = state2.wireEncode();
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(block.wire(),
+ block.wire() + block.size(),
+ block2.wire(),
+ block2.wire() + block2.size());
+
+ BOOST_CHECK(*state.getRootDigest() == *state2.getRootDigest());
+}
+
+BOOST_AUTO_TEST_CASE(Combine)
+{
+ State state1;
+ State state2;
+
+ Name info1("/test/name");
+ info1.appendNumber(0);
+
+ Name info2("/test/name");
+ info2.appendNumber(1);
+
+ Name info3("/test/name");
+ info3.appendNumber(2);
+
+ state1.update(info1, 4);
+ state1.update(info2, 14);
+
+ state2.update(info2, 15);
+ state2.update(info3, 25);
+
+ BOOST_CHECK_EQUAL(state1.getLeaves().size(), 2);
+ BOOST_CHECK_EQUAL(state2.getLeaves().size(), 2);
+
+ BOOST_REQUIRE_NO_THROW(state2 += state1);
+
+ BOOST_CHECK_EQUAL(state1.getLeaves().size(), 2);
+ BOOST_CHECK_EQUAL(state2.getLeaves().size(), 3);
+
+ LeafContainer::index<ordered>::type::iterator it = state2.getLeaves().get<ordered>().begin();
+ BOOST_CHECK_EQUAL((*it)->getSeq(), 4);
+ it++;
+ BOOST_CHECK_EQUAL((*it)->getSeq(), 15);
+ it++;
+ BOOST_CHECK_EQUAL((*it)->getSeq(), 25);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace chronosync
diff --git a/tests/unit-tests/test-sync-logic.cpp.outdated b/tests/unit-tests/test-sync-logic.cpp.outdated
deleted file mode 100644
index e916ad5..0000000
--- a/tests/unit-tests/test-sync-logic.cpp.outdated
+++ /dev/null
@@ -1,181 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can 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.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <boost/test/unit_test.hpp>
-#include <boost/test/output_test_stream.hpp>
-#include <map>
-using boost::test_tools::output_test_stream;
-
-#include <boost/make_shared.hpp>
-
-#include <ndn-cxx/security/validator-null.hpp>
-#include "sync-logic.h"
-#include "sync-seq-no.h"
-
-using namespace std;
-using namespace boost;
-using namespace Sync;
-
-struct Handler
-{
- string instance;
-
- Handler (const string &_instance)
- : instance (_instance)
- {
- }
-
- void wrapper (const vector<MissingDataInfo> &v) {
- int n = v.size();
- for (int i = 0; i < n; i++) {
- onUpdate (v[i].prefix, v[i].high, v[i].low);
- }
- }
-
- void onUpdate (const string &p/*prefix*/, const SeqNo &seq/*newSeq*/, const SeqNo &oldSeq/*oldSeq*/)
- {
- m_map[p] = seq.getSeq ();
-
- // cout << instance << "\t";
- // if (!oldSeq.isValid ())
- // cout << "Inserted: " << p << " (" << seq << ")" << endl;
- // else
- // cout << "Updated: " << p << " ( " << oldSeq << ".." << seq << ")" << endl;
- }
-
- void onRemove (const string &p/*prefix*/)
- {
- // cout << instance << "\tRemoved: " << p << endl;
- m_map.erase (p);
- }
-
- map<string, uint32_t> m_map;
-};
-
-class TestCore
-{
-public:
- TestCore(ndn::shared_ptr<boost::asio::io_service> ioService)
- : m_ioService(ioService)
- {
- m_l[0] = 0;
- m_l[1] = 0;
-
- m_validator = ndn::make_shared<ndn::ValidatorNull>();
- }
-
- ~TestCore()
- {
- if(m_l[0] != 0)
- delete m_l[0];
-
- if(m_l[1] != 0)
- delete m_l[1];
- }
-
- void
- finish(ndn::shared_ptr<boost::asio::io_service> ioService)
- {
- ioService->stop();
- }
-
- void
- createSyncLogic(int index,
- ndn::shared_ptr<Handler> h)
- {
- ndn::Name identity("/tmp-" + boost::lexical_cast<std::string>(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count()));
- ndn::shared_ptr<ndn::IdentityCertificate> cert = m_keyChain.getCertificate(m_keyChain.createIdentity(identity));
- m_faces[index] = ndn::make_shared<ndn::Face>(ndn::ref(*m_ioService));
- m_l[index] = new SyncLogic(ndn::Name("/bcast"),
- *cert,
- m_validator, m_faces[index],
- bind (&Handler::wrapper, &*h, _1),
- bind (&Handler::onRemove, &*h, _1));
- }
-
- void
- getOldDigestForOne()
- {
- m_oldDigest = m_l[0]->getRootDigest();
- }
-
- void
- getNewDigestForOne()
- {
- m_newDigest = m_l[0]->getRootDigest();
- }
-
- void
- addLocalNamesForOne(ndn::Name name, uint64_t session, uint64_t seq)
- {
- m_l[0]->addLocalNames(name, session, seq);
- }
-
- void
- removeForOne(ndn::Name name)
- {
- m_l[0]->remove(name);
- }
-
- void
- checkDigest()
- {
- BOOST_CHECK(m_oldDigest != m_newDigest);
- }
-
-
-public:
- ndn::KeyChain m_keyChain;
- ndn::shared_ptr<boost::asio::io_service> m_ioService;
- SyncLogic* m_l[2];
- ndn::shared_ptr<ndn::Face> m_faces[2];
- ndn::shared_ptr<ndn::ValidatorNull> m_validator;
- string m_oldDigest;
- string m_newDigest;
-};
-
-void
-checkMapSize(ndn::shared_ptr<Handler> h, int size)
-{ BOOST_CHECK_EQUAL (h->m_map.size (), size); }
-
-
-BOOST_AUTO_TEST_CASE (SyncLogicTest)
-{
- ndn::shared_ptr<boost::asio::io_service> ioService = ndn::make_shared<boost::asio::io_service>();
- ndn::Scheduler scheduler(*ioService);
- TestCore testCore(ioService);
-
- ndn::shared_ptr<Handler> h1 = ndn::make_shared<Handler>("1");
- ndn::shared_ptr<Handler> h2 = ndn::make_shared<Handler>("2");
-
- scheduler.scheduleEvent(ndn::time::milliseconds(0), ndn::bind(&TestCore::createSyncLogic, &testCore, 0, h1));
- scheduler.scheduleEvent(ndn::time::milliseconds(100), ndn::bind(&TestCore::getOldDigestForOne, &testCore));
- scheduler.scheduleEvent(ndn::time::milliseconds(200), ndn::bind(&TestCore::addLocalNamesForOne, &testCore, "/one", 1, 2));
- scheduler.scheduleEvent(ndn::time::milliseconds(300), ndn::bind(&checkMapSize, h1, 0));
- scheduler.scheduleEvent(ndn::time::milliseconds(400), ndn::bind(&TestCore::createSyncLogic, &testCore, 1, h2));
- scheduler.scheduleEvent(ndn::time::milliseconds(500), ndn::bind(&checkMapSize, h1, 0));
- scheduler.scheduleEvent(ndn::time::milliseconds(600), ndn::bind(&checkMapSize, h2, 1));
- scheduler.scheduleEvent(ndn::time::milliseconds(700), ndn::bind(&TestCore::removeForOne, &testCore, "/one"));
- scheduler.scheduleEvent(ndn::time::milliseconds(800), ndn::bind(&TestCore::getNewDigestForOne, &testCore));
- scheduler.scheduleEvent(ndn::time::milliseconds(900), ndn::bind(&TestCore::checkDigest, &testCore));
- scheduler.scheduleEvent(ndn::time::milliseconds(1000), ndn::bind(&TestCore::finish, &testCore, ioService));
-
- ioService->run();
-
-}
diff --git a/tests/wscript b/tests/wscript
index bc847a9..384dbba 100644
--- a/tests/wscript
+++ b/tests/wscript
@@ -4,11 +4,11 @@
top = '..'
def build(bld):
- unit_test_main = bld(
- target='unit-tests-main',
- name='unit-tests-main',
+ test_main = bld(
+ target='tests-main',
+ name='tests-main',
features='cxx',
- source=bld.path.ant_glob(['*.cpp']),
+ source=bld.path.ant_glob(['main.cpp']),
use='ChronoSync',
)
@@ -16,7 +16,16 @@
target="../unit-tests",
source=bld.path.ant_glob(['unit-tests/**/*.cpp']),
features=['cxx', 'cxxprogram'],
- use='ChronoSync, unit-tests-main',
+ use='ChronoSync tests-main LOG4CXX',
+ includes=['.'],
+ install_path=None,
+ )
+
+ integrated_test = bld.program(
+ target="../integrated-tests",
+ source=bld.path.ant_glob(['integrated-tests/**/*.cpp']),
+ features=['cxx', 'cxxprogram'],
+ use='ChronoSync tests-main',
includes=['.'],
install_path=None,
)
diff --git a/waf b/waf
index babf365..ef9df38 100755
--- a/waf
+++ b/waf
@@ -30,14 +30,14 @@
POSSIBILITY OF SUCH DAMAGE.
"""
-import os, sys
+import os, sys, inspect
-VERSION="1.8.0"
-REVISION="8b796c068b3476c699246f84311031f0"
+VERSION="1.8.2"
+REVISION="a01aa13963437a19e5d8748cf2afd71b"
INSTALL=''
-C1='#.'
-C2='#-'
-C3='#%'
+C1='#/'
+C2='#.'
+C3='#)'
cwd = os.getcwd()
join = os.path.join
@@ -54,8 +54,8 @@
print(('\033[91mError: %s\033[0m' % m))
sys.exit(1)
-def unpack_wafdir(dir):
- f = open(sys.argv[0],'rb')
+def unpack_wafdir(dir, src):
+ f = open(src,'rb')
c = 'corrupt archive (%d)'
while 1:
line = f.readline()
@@ -128,8 +128,8 @@
pass
def find_lib():
- name = sys.argv[0]
- base = os.path.dirname(os.path.abspath(name))
+ src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
+ base, name = os.path.split(src)
#devs use $WAFDIR
w=test(os.environ.get('WAFDIR', ''))
@@ -152,7 +152,7 @@
if w: return w
#unpack
- unpack_wafdir(dir)
+ unpack_wafdir(dir, src)
return dir
wafdir = find_lib()
@@ -164,5 +164,5 @@
Scripting.waf_entry_point(cwd, VERSION, wafdir)
#==>
-#BZh91AY&SYZ"ñ¶ÿÿ¼Hÿÿÿÿÿÿÿÿÿÿÿÿ"¢§PÀ#%#% >Xaänöº#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%篬6¾Ü®½u{l2µ =Ú½¸ûÜ^ê}k³Rõå}ÙóÜ®|湶ÛK,£»ÞòëK¯}>å
õËu¨îoeaHvÙmnØîÓ£§£×»Q{Ü÷½Õm꺮ï7o]so6ðÞvݹëÕï:#-}<öë¾÷{á×@Ô_|l¾î¶ñ5§o¸µ3Û>»ì#%#%#%#%>û#%#§¦ì{>@ }óç¸K6³ÝL½Ûíco`PtÑ£CYïv ж
yÙÖ
±ª[GG{¯Gªq% ¡@ ÚCRJCL¥*°#-zÑTkÖêv£¾ßv×^Ö/M6·¶Þ÷KÓ#%µ;:Ó©]µ¶o·ªv[ZÇÕ2¾<vúúõ÷o«o¾³Ùݯ9w>íÝ<ßw{×o³l¥µ¸ûï_cß;wzõyîûíÅõ×hä#%ú3±@#%](룦ÙíÌï}Þòzú:)ï{ª%ìi(¹ÜôÀëEU é¨@Ð7[¶k_Nï¬×VÙ½Ô#.{¶·{;ß{æû{Ü
OfÛÖ·±£çØ_^êùÝÐ>sN(øÏ{+[kÞí#%ówrzß|ïUY#-Ïy^ØñÓëÝtÔ#.!Z;îñᯯîzïªçm×˯W¬û«ªl}ÏvÕî3]uõÞÕÞé÷·½Fï¶àÃÚwÝ÷¶>¾@µ×7·Ï3ݹ4IäzRóÑÖ¾=e²½ê)PªåÜÍñîç>Þ÷{ÖÖon¶ûÚÉóëRÉkës¯¹ËÝå]åí^ì}ïT{Öù5#-÷¶÷Ï}ﯷy¼ðöÏ èÒ¡CÖP#%¡]S&eÊ®ìÝ«qï=zh,ÆMuµÞs¯ ¤ê«Õ0îÔ'uWBìd#%ß{Þ<@#%&½õ˵ö¯o½Ù÷zú϶ûy½ïzµÝÝ渦ê»:äºètøì÷w9ëÕm¬]ïb!Ýp{ÎÖ÷gy>öð4ë¼ #-½Ü:yÇ»}±Ò#.¾¶òíõãìï{u.ßW×}½Íî·sÅÎq»íºÛ«Ëì{Ö÷Þ½<öúÏ3ªéît¤rîîçO1Åá¼ÝPv±¾ðú§yñ7nÃö¯½7cï@0}këbbª]yG¯}ÝáC`îMQ<Ø^ݹ[®÷ièõ÷ÞÎrNë¹Ýݾ½
×gêîîØëES躥 ²¼ÁÝÕ·¯uõÐûÏ^Ïí qÝ·®½æ÷¡ô:n÷#%Ð=æ+·÷ ïaz£Ýñwª#%}d¡[9'ßqÍ@Cݾ½cU¾¶»ëκn{g½£]u8]·cERKk¤K#-Ù÷¸®}pvlô×K!®ºkÞw}Wuç¯6>÷ÊîÖæ|ôÏa¥éïg¶»8´k£)uÓ×o½Çvï]Ï@>½^ûjwkòë½ÚÝçeøÜA#% Ð#%@&&@#M3C!õ?Q©¡é#%#%#%JhDÂ#%M ¦ôÔSƤõ64Ô6I õ#%#%4#%#% @%?ÔÈhM£ÑÚM=5zCM¨#%õ#%Ð#%#%#%#%'ªRD#.dh§Jz¦h¦FSÔ4Ð#%Ðõ4 С ÓC@#%#%$ h2hÈL ¦14ÚG¥0i4i£F#%ÐÐIåPI¨ #%dSÐi2z(¦Tô50ÄcM2=HÐ20F&Oÿ¯ÿfÛjíÐO÷r\ÝÚ¹´Z×u?Wm©]¦ß¶§@!$V£+f{ÕV«óííÛU¯Ùü?OÍ=~jIýéX¸X!ëQ;Õ#*)LS\MeÎ82nÛÇ.¯ï>±ÿ(øÍ!°8 ÐïUmûóÍÝ¿ÛW#.±*{ºyª§ W¨©|\,båðg3¨#%}ûèú!Â*0(È°®ÛjUFµmÖ¢Ûf`$@º¨@DyÁH¢§0 Ð#%!!A±Rt¢ eDjYÑ#%S¤X@#.&bXÌÓ£©Y¢$¥H¥&JlÅ5¤¨je5BBi-JÉAFP$Íe#-d¤¬Ê±¡cBi
(64b-¢)%-)¢AYi-$`Ê"FÑmIªib&¤bQM"F&±hÔjVm²X&I*-@J¦ÛF&lÍ4ÕL²Õ*[5I%HiLI¨¶djFkE¤#dÓ(,IF&¤ÑJU&±)1)±C©#%R!Ì@CadJ"L,d#KRDÈB-"AQ)IjÔ&Ô#-"¡#.(¥ `F$Ò%±1±²e6I-")"h1dRFIR)Ò4M$Â@¦ÄT6 ²#.FJ*6"#-I¥* $ÔRF@@2fbY¡¡,b$"©6$FÁ2bÊSÍDSB¢¤J&H6¥e(ÁHi4²Mf4¥Med$U#%ÍK$*$²ÍfÚ2ÉD%6h´Ðhbii5&JI ¦JY¨ÒYR¢ÅQI¨3 ¦#%ÂHÉ#-0Ñ$J#-5¡¨f²ÌÒ,#.R´&5-I&%#-"Ø6,LbÈLÍI£A)°ÌÍ$2#-L-,bÍMJQfQ)bL²*HÙM1h Å&HÃ$Ì6L#-&eC!VI6ɲP$¢L¢B)¦2HËÒR©³XبÈ&D¤¶45CI$64H bj5U$#3E&Y$ÂÍ"#Q¤ÈFTfÙj¦µ´Æ2TÐ-%ÊHMLQl,1Í4Ù)J-LlÛ#.1Ð2Í-~n²´BYFBkÔV-&Æ̨¦FÉh*´ª6#-,FSIÊÒT2Ô%¨²)¬ÔeEabdÌjZi±#.̦ÛMdJ²²*ÃXÓeYQªÅ*ÒZB³ek5)LÙ
YÈ£MJ-2#,Q±XØ©-clUl[hH©(ÛcQ°Y(Ö"%Q¨4U¢¡µJÉ#."´Øe4R ¢3RM©Tj6I6ÈXÕ4[VmZh#E[FË"fÍdIP¤¶Ùª)³54©Ò©i6©B¬©¤Új-,*)lÖfd²Å¤¶Ód(µ4@Í%Å2!AL£jLL,ÊV-)4بÉA¤¥(¤S*"Å ´²Ò F)ÊlÐÆ B¢Å!6jK&,Ò`Ù#-DÂ4`Ò&ÄERQ Ò4Ê-L´Ò)+a3` ɬL1³-B)S4RE")´d,Í£E4#.ÒZPIhRËA&6M%#)°d¶33RMdÛcJ¦f,66Rdm&QelQ!Y(I6RK Q¨ÊFÌÒ!*k#6*#.ÌÙh¨ÄؤÓQ2ÄÑS-©e-ÉÙ "cBih£#YI²R&"jI-*ب¨ÑFªi0É3DE¢#%ØÉ%Ùi¤¤ *5a¤¥#-6´j-²ABe
h" "²kc()5JQ5b±ÌÐÅI%L*J$´l²ÊÕ*F1%dÔSKRSAD,¤¥)J6ÑUm,i*%%dÒTXÚÙ¦Q³M`ªfÉ,ZfJ£¥2ÄÚ¦¦fSY¥-F±¬Ã$Æ#)Th¬É,lh"L"J´ ¢Ò¦VJ¬V6Ó1¬²@V¤ÔQ¥df4ÐŤ¦#-2ØFÅ´lRm%Ú ¬`2j*$+DͲËQ±´Xµ$¶¥²ÈieaTbÑLØÛf¤¥eFPÒl©±´Ñ¬I¶J6ÉmÌ£I´hR**J4Z6ÖÙµi£5&fQhØÚA¤Ù¬ÚLS*ÒQb´m±h¶Ú6¨eµ-SR²Ø)bE¤Á²°²Y#fA3De$ÈZ´ËTÇÇý¥^þ?Ƚ/å_ë=°µXI¬×6ì%%¤ÿmþMH¸aÈÕQ&%Ra?±?ÛE2áÿøhùJPØ$\AÈöçQlþ¬^ÙéH¿IÆCtP¶Cd£J«JêMÒÄPwfB¥Ïøª~°ÔµÝüµáøÙeßñ¥$É¡»*¥ÕÜ
ò;Hh£¸Yøïf£S¸OU¸ÏÝ!SºqÖÅhâcQ c<|V÷j(ä6Ú¢ZUUYe\Dorõ3YQæÉKL§Dk@ªîÖ¨ÊafRG%5æØ2¨)TQi[5¢¯½òzõ7¦ÞÂæå£bÑlÁ-¾^HóRjï~ÝåØóº¡þ/W¿³.nRnsíç^!éÅ:EAÿ¹å¨Æf0íTH8äÅË1=e DEøÿeNÝÕ£0´¤Mê}(¡¨0xçR¿¿VªÉ-ÊÃP`Ìõ Ý&v_Y¯¦ÆM+fe> ´Ü={\ÛýZü#¤®-Í8]¨"0¡ùíæó$×;¢î4W74B¬lXÚ?
/ªUìµzZásɲ&ZdÅV
JEÓPÌd÷²Á°íQ è0V8çÊ_â6½ç¥îµu,´Ôv(Æ5|>w»¡ º#-îVX7ªT®#-=È@n\¹&î»ï¾òb ßÛ×ý+éw5_w·N·I²_'|óS¯ÝTÚÇì1éOú¯L,ðU&É'}f4#-°¬.D¨Àn2Wò1#%ÍTDDÐÆ%É
VQøSßhhäMÖí§:ñbPJ2!×eus#-¥QLq½s±æj)þÝjÏ
_6LoC¡#±Î¾·êC_ÂÇG2KE? g3ªP°Í=nN¸~rZ2í¯V6ÅÙªÄÝIÞXRÁ/=/Wå0ôIQ:kE5#.BÆç±S+áÌÜϦ÷j@Ò<òwóû÷»¯a¦É1üb)àföQ0æë2¨¿úÜoÔHÎÒtÜ+m5øúR¦4d»>ÿv÷»©´Th¯ÉdÞ+¬j¿Uå5}Þá¢Óìêû^oöû}ßÞí}/k´G-Íkº»b"0a*ON\¯·×Ýöõmo&[ByV '
-%!&ÇÓDf@îrØÅÁxã(ß5Ìø¸¤¶L%jÖí[ ¤P9¤Ù#.Eípch²pô»|u#-ÿ<p°#+_;ÚXþM~Æ.¥Ý;§_ý#r®¦ùK©!M(Áa·K±dÜd)7ªpF´å¥F(Aî ÃMiM ¢¥O£#-*¯
Øh°4BgwZ² ¤1T¤ZëaɼH¤xU%»Däâ WpòÄ#-Ù£&ZKª(ÍJuªS«FoÄ^#-"³ÇBy3G×Ó
"wÕ7áRüjÉB|áøÝËéð©óÐ>îÇ»Àü"VéõÒòìøò?|>áH ùgWØÂhEïú¥|dT=pJ$R4(§{~%vÒV gÚÔaÚn#Ó~7Göß[ïºhþß~Ñ¥Ù(ãvfþÿ¶¹3L¡èÐ)Ñ(üwöK§ÆZk/ê×l!SIÁ Ñx%XÛ±¹H<xÂÉlײ!²³ ìË÷U.8Í×ôQSnkÍò£ã»ÃaxI £ËþÂv¤,©åÉ!ã8úñ|öݶ|¬ÅC¾ÜIö¿,ecßi©3è©¢ø36ɾz9¤!Q½WdÑÙíOCÝe|ä(ô¥% Æâɳ)õò©0æ©_ÀejOQ`_ÀÝrtÒ4\|¼ÇcÇ'}Ôþv·äçxáp3ZCQçs1síP8ú~MsÎÖ
j¦nìe´ÄîªEøûêßCÎ{¼¡ÿå4Ö¾¥ºDdåÁ;@kÃó¼\®ï#.6ôÒXà¹Jëxî`¸ kì«=Ù;i¯v3Îém´e%Ý¢
Ó
Ì8~ú<¯¹Á*>ýk¯0ãUƶ&#.jb<}¶L¹yò ÕÈ ¸y:8Q×_,Né2*ÑûÿOãÌÌDß1(Râ®y»wõy3©¦#.e:#.#.FSuõÜbjgJ=çr·y:¢Sèq#-¯J,sæó¾i/"ý#(Hë\4íìþ±O#»¯°p
óøs#-+ÏPãm¡zªQ!££.x(Î&l9^ã->ix%6±Q4©»`»·u:ér|úß«>¯Ï?CdPDMή·ñ
p¬òLC(]0ÁM[Kj4bËæÝë»A?n³ZLúýü¼rêB^òn(2vkîåý7¿oó¾+?¥é(åâÇäÀ²#-Ä×ÆФÚKºYîg°ºþ'ôOjݱQáKëM*a¿uAÓjÛ9ÒHg#-±!ù¸ÝÖéKÛ¶dâ#uA³ÞÿWÂn|õ¼4Ú¸G§>[¡O·2¡Ãó¾íü°Òhh]#.Í
µ»Fî#-p|3÷hb3åƵfÙSÑ¡ÎWõ¸B84Øã¶OÂôÓ¦)8¤£J)38¶&Q]«,N80YBOeÙëÍkõgÃ4=ÀmÇ<}W®@Øðklü¨¢-ß9´/âñàlÞO^Tpi!SuDÏê²Y$Çö¸Vüõ2TO×W¾xèRW[³FyïS8)]ºV5ëw î©vb`EQUhËz<ª©É%#WM*üäÙÏ¥SÁÆ1s-ý¸Rvö\CÉʳM1³.nPïGʤGð1Ó$&íM)ý·z·ßÅÚx/y(rX@¦<ÝÔ¼äÁN(¢f:}±#-È/
0?²D{£/8Å2쨧£<HõIEbY
²Ñwî£ùã(WcÌOrJ_®qãjgDkí*ÚÀ{Ó÷¹kpsÍ×2?¢ÿÞ$ÓJ4Òw×R-FÔú;éÉM?)èýbá¸Ûí©2 Cgn;è<¢ß©o7!P:#.g#^+²ù»¶.Sôúþ®øf&ú#¤¥òë¯X>}ÔÖe;Ç˯PUy5éI©átîß¹«Ò½FQÝf÷%@22¤F,ìË'ÕØôÑnEò}GuÖbSxS.¨66;¹²Ds¿¦çE[-Ïo7ÚÎ\@´òM¶©«ßôül/¨Q¿[ºPàxþú]¾SÑ;ã ÕÏ~ÒôU|?#-ÕÙ»¹³æFÿo>ú]úIÏ7~
©"\JyzR/¾ßb}®gCýÓðUYW%Ü(ÀmRhæò
öFWãU#-Îí%×{H-Óͳ®êrͺ¡X³¡ÎmȧIÕ£fçM~JQ¿wS9?ëÆÙº=/ÊàéPÍ(tE|;^1AÜͱ¥YmQè=Û÷?¢ý4âÆþ$ó è$Þ§2G@h#.ÌÑMÐñçÝÝ×NÉ<¹`²JC!÷|èSGO¤@|_òi´¦V®B·¾câÀðoY¼!dLñ|Ø%³ºÚþTtìõå tètîÓÁ[ÝÖ'"ó´º}`8¨g[¼kP¶!8'¬¤|Gam&÷m/D Ù?!&\¨Ú>Rs+«R±öñHÐpF"áÞºY,P¤Èögïgº`·çUuB|ÖÔV®ðѬÖdcZð¨DØò¸sK"¨å4º
2ªJkï³ïîÒXqCX* ¾#.ÅqEèè:`Uë/»²ÏÏ ðá3£_©íRwKÊç}߸C¼§dB>¿ªHºr<9®h![ÿ`áTÿÆ~ëe9Þ¡ÁûEmêl/ç^µ#cÅÉÊWòfµÇ\#.%JÞ¯3#ôk&&祺ãpK«2³5µ½P¢}©e}9¾ÉÖè~ÉËë&µÉîã#.ÌqÖ¤_O:Ì1"ÍÄÐx»ö¸âþx©|/\Ѷ#.clŵdÉGòZIa,áµ_Í(ÂRÊF¥']sÎ{xTÜ>½x9¨xó³\#.c%ÚN_[èG¸&Ð+¹ád\¸\x\üÈäïbÜa ù< FR!:ÖÞPÚ|Wñ¦õ/JêFLÊ<¥ÔtÆfaêa¦=ù¿¯u|}0ü×|ÍF}uç*ÎQÚ¥Þª¦ÔnÚÊØ*½ÖS#-RnÒcÏ#-Æ·hW³:bXäpgê§×Ö±ZÝ×HyÍ@$ÂXT÷)³âî6''a@lÉõPë|¾K="_.p:Iç²ÑD:[R°*¨¶]¨(}G+X¢hÐè²V-<M`T¢i¦@tj2'2A¦îÅe ©(R¨(¢7W×T½[ªJ«Ç'kÒÐ#-4Tlµ¦Seõ÷õõcGIRåÍókn!OZs:Ý!ÏòNK]ÑSk
£üzéÁC¼Ðßûyíd(Û~Ý¿·ö*ÅPÙÑã uóÛÎL¢ã/T;çòÓüîÊ.ru±Í+«K'ï¿*~¥`lx¿b¢N5f8¦ ©¾äÆV;E½¹<ÔÉÝL1¤Î¬½Ùh6Ð(ÖôRÙH3)¢tµY©8â¦)¤¥Ãqa⣹ÑýôÑôd8³Õ¦¹ÝVú!8Õo[>Ö ÊeÂslo5êÊ-'îE¹ä£7{Üõ¯ëuªn,ûÇ%ô¢xRÕîÌhi¥J%-×GeçTÂrh-Bó_ªÕB¯ãÄÝ.<¾«²p (EÈ1»gäËV³j¦¨é6CÓ{×~çf(Rª¨§ûqÒ]|º]§ó¯õêv
zômÓ=?Å:ùÓ{(6}²}®êÖËîû³44jWB;Í¢i·£ÉCg½ÚÆ#Ñ\F7é>9m²ñ²TS¥BãG{dêȲMP*h¼å£BïAV9ÈÄjM®ÿòÏ.lê2ðë8~»çquØÁ%ÁÏGn5Ó#HèüÛG:kh¬¿v"$BB(¾ÎPÓH«½íÜòêDÓbã%_T4ÆJ6*æçðN~3CkOiȺt§Þ@Iõ%䦵-ç¨P>ÿ/OÚkY_¯\èùqäÛ=å¹û÷á÷sçðç1³È¶ôGäÏDOÿ>¯G°ðȨt¹IÑñð$D96çü[ÖÈÖ0åéî9¢)p¤Ñºv6Ã;eR;íίìÜwïÏ.»tL3ÐêWZ!5MØN¼h5yÅSø\|ÍýÎg®åëFèç½ä«ô©C
pº%péT¦iäð¥ô¡ çs>2ÊZiíá£lîwçÑnZuÔ?ÏÐìWC³êyQ¸ÿB¦~áz[ç÷ø}ýk¥Íñèò!=ê2õ*Úe§ðvs̤¦£8ê
2o+¼"*¹ø*ðÎ55ÝZb@ÿ$[,ªÙPG¸%F<ÿbû_Lé-wM½2´²0÷m#.H¤}([üôuy½öQÍ"'í7$!)$èkÎ=çåͲ¾)F¶ÂÊ#-Ц>æúG»vp
½¨ßÆ|Q7~TBdB7oð[ã+ò'×}óôÎQ¿dͳíó/JM×51âHåÜÎê>gAùEÈ;xî:é¤Å"ð÷Þa/Ï\ã5ξZiÚú¬qs¸P¹G=#¦Ñ£\µ=ÄybÔð÷ã#.®«¡MîΣüÔxéàóz;$¸s°½Íçz®TCæG ~éñÐ`ý%ì¬i³¾$ªj{;×ýï£D#-ó¦u2Ñ\ZãE.EYÐvÓ«}zX+tf¥k|¤XÆ ¨¡Ú5TF0ÅßëHåCZ5ÿo]×nÙ²40´·ðT*aF¢1Âçk,9Ñ:wBQôPí~§ë×FRW*`Òéz«ük¬)7þfjcù¦kùoËìÝ£ÁÜÍaþQcþAFå«ý?ß#.<jãd9#%¸ê|`ðeGKa¦Ø±B7aùÎÿ°Výâ/\y_s/Ws2¡·ïU#-¾Ä°|ôò`$BDË Õÿ&ÝAogdTßʽVÓêµ@J\¶hð v¹ù¹ÍZk¤¢2W¯?WÝöVtul5Ã"RØîý¹ï´Ù0pÁÀD7e4p"Qôm>ãCT,eÉ$X(?ëÎmÈþ½Öõï¶>WsE4`©p#-1ý²§ð°°ã[i!¡¡D£Å?ÿ~ha#-nü¡4b¨{íüª3÷oWI¥oÂJ¯»iR;y¥[ѧåæ±*ËQ*
GºÎ8ü'DQò~Þ)eä¢êÏóé×Ëu÷iÓ}=AAx¦áM(¦ËAASëOC}3óÚe-æiÃ%Æ«z¤¼"K,G1تüûv[$¯¦iìñ#ß}zý3ôΩÑeRÇâüæÁÞ!#. u6¤ ½øù6 »/WíðÅPýþz<*¶kZ¼Ûß½õvß{ãStËöÀÕBPº:?wË:¶BûØ_Èý:ªÚÌ#ïËõ»F_Ð?ï½b¯>\}Û2§-?ÚÌѯ#-ñ±ºgDI²A¦HjêÊÜJr[ä[¿¢µ¨4UO^8¦i¿áóç¶ucTÍ|>cûT^ɼtÙ)þX>6pÉ-£,³Gâ8üuA*"Í÷bé9jθÛmE-41wa"özTaLt¸V]û~Ìÿ^G²¾5ýÓôàÎÑÂPt¨mIÓwq¬ÔþN¨´ÖßÁ8ÆÇêë5ÉpAçVîÌ88l-§GJÕYʶ^êóèá]½Úýo\eqbBÓóåz@^ø¥¼vÒÏçöéaÕ4$t]ðw>hlËãì×rÎñl¢%ï®É¯OW/½ÄÌ <}-Ù¦E, JHsýè1°÷`òLÃ#.=(ÎÁ»¶ ³E1>qTÆ`ûngϯz¤uÿf?HÙ±|´Xfw-}gªâ89ÎÐÒ¡;¾·h#-¶ì>nÔHéºù^O]9#ÛüÿLÛ(xÈ@JW8Ãàï¢#.j{^#.õ<5jÃÜjÝ-;wÇ0öýøÅj4Ð=l{m>üæÅãa
NZ§^ÖÙ5U-U#-Z£O[ýÿÓ\ºÉz"hÃÊS3Õw^ÛfüÌÌ{¹2|×&ªvà f}ÉÓ*=æÛù´+B¿2¨wS¶eÈÙ{ÄuÝó=b×æ#-C¡\$ßÏòÜäkoï|tï¬^æ±üÝki'\Ì×ð=ßÇ¢Zá©´l%)hU:Ä!R6Ôf:§üzúpaÞLrvÚ;Fß6¤!/r|uÂßztt>§1ýÜöRJãn6ÀA¹(Q^Y¼=ÝÂo¢ºjË'a²aÀb[Üwí8?}¬zb¹µÓµpÀßÆAðIä<:-iØYÛìæú¾¯át3c^:¶Xp¿S-±cÎ×w×:ÔÞ¾3 5ؾCV&#-zx?VþEÕ5ryÚioÃ,vrDÒÕ^ØuæíòÓ¶ûbE§vK$Ã#.Ý8Ë/Ô95ª$\^_çõÇZRàfÚA<Ô\§Xuh-Q¦VZñs|X¤f(¡HV+10¢Ý2©¨4Êz"Bt0úâ?s·1Îqíu÷/öb,D@ó׶ÙÈì$¨ :>ãR£±©Â:l×îÿ30|õC#-¡gBEK};±p49äyäît×ßaxÖ{wÖÃä-#-Iò¤ùDÜB[TÄ-0Àvd¨>GîÁ÷©°Ä?Wíîõ)Y}Kl±¢«c¢ÔeùµÜ$MXW+«è¼³V÷Ó#-þHÁ~wé¨IC&ÆÎu/Ç{Ø,5Èç=Ä7æØQdYSÐÍÄn±°¢qÎâÔòóã+#-âyl[µ>+ÇræoÇÑîêjmÕ
þ>¼hÝÍ»\¡À\^S;x:è]S3è[@wë.Ró+zóÞØâwÏS@áä%<r1ïåE×[cÉ<òu@¼yÙÞh,ÙÜÁSeQ±§&><¥rpÑs¬/¹Cú#q iZPõXv j^:¾êÏ£çßV1ÓcÕÚé*ë;k6nBG{{y¬+ÒBøÆ#.§`xbÖ¬¡kÎq´Éî]þ}MV£8gÇGtÆÒß<{?)@ñTl}ß·ØÎSXdØ×ÇlcúH|ÏDfg>¯'ðÛ©¯©öãþTT}^笷ê}°û5Kóh×~uzc~¶ láMÑc<zaºkº¯;sLI-T¨$¤"i¥¾W Ñ~Þ~|_vp÷éñãÅ#-0i)¢Æ¬)#.L¦aXÄþòÀÍl&tc¤÷À>T<¥¼ñX¬7lì[K´&%:#-ãxcZ2=BNI?ÙøQ[æ7ÃѬ8C|QI ,],3[v³¹@ã{Ñé߶ËÒjý!òå%N"m$Ú#D$[«X7Ñ-²}k"."b Q@ðÉóÓóFKn9Aé#.DùQÓÓÃé:çDíúð¤5¢vçÚ$cg¤Q-ר¨HMTìÎèøYqÞ¡«{éÅõÛdק{û°pïTÓgjD¡;ünhÝ-hÞd Ã#-s¤ÄÊg÷ÄtM"!ÍÖh:Yо)ªÂ3ÐÐÎb±#.JÍq²nF@ÊJ*Ð-)CPü :ÏIXª+¢KES«Ýc¥"ðáBfb=ÜÑmuÊiûn+aha.é£/Û©«Ò5¯à¨¦XÖZUz&×-||Ǧ¬¥B&D
·S+KL{¨0m2CQ½ú7§ÇùHü<wÜ®~:N:ßµ+\¢âµ®©ÓqE»ÉÎøQÖ«ªÙÜ«ê¦úh«j2A¸ÐÆþý¥NÍä_Ù§öÑNÛúµ¶ÊØÓSxl!øk¿!ÿ{¶î¤¢5ðê
È·ÛOÕé÷ú³Ï$[±î63~átݳr/b67JP~@É`È,b ÈãÆÉxô³îÈz_㬩ѪBîåKxà°Ë$0ÌS8sÇML»U/ÏyR}³f7¬
£|[ø¹¼üÜvû5ñã±õ¿JïÏ+ ÙiüÞWÖë#-¬h^TtdwÚ!µ$Àh´Ç?ªáû:¼tZ¶ã\»ùϽ1>â`"`ÑÞøsbì4D¸ Me8Rq_vÊpd6# TÛñJ¢0o©èg&#-ü%ʧbüÜÄí ß,'c²¶ß-.][æR!ár¹eAè
³óÃü£{qzCn<'í!rñ²V¤]±Ú"¤mìðÍlzcsèû^Î
Oô÷:iéÚiJv²ÙitkÍCÓ]0ÅVÂrVí&cG´õçNÊ]®^þ2¨ÝTð?1¤cFÈ+MìͤU4¢E$XdKMÁ4@Ö¢ph#Cjå
Ee0pRAƸ5öiÙq$«èmÏå¥iD-éR+ü\ÅOÙ¦º¬ÀÊ\Å]Dà4¹Gæv\'¾-§w!ÚÄNªú·ËWaøyØ]®c4õP5v;ÎÔÈ|~çÙ#-hM\Ô
²-f¹WHXÐÑcÅKÕMñ?-òÁ Ãyl@ÈéP.Üð£¿ð70m.B `vÏ[¦X¸QÔ©EiÖ*ý^çßë³L¸¨¦d¶Èýé.±>RĪ-¸ñ".¦xç%£MÎÅoñFõÓ¯ª²n9þ±¹9ëÍÔPí>ÊàUAB¾JF+7:=ÔòÊÔ#-p&#.Þ1$¦ ÝñNnÈÉÂ!Ìý=Að{jÏqUùÊ!l,
J/MKNZ]5Ùç(¢DY5#ãæ¯'»gØÿ8#mÏï18át:ÎË<lBHpZÚ¦åþGÉwðö<¤>ÝÛEànvàCúêÛräÊ´¢Ì nª¢*¶%ÌüÃ%!õä,8"¡`ëO,çòßN#->=ä®æö|ü×Õ)b2äOÇ*l?ðÉÐFìl[fu§f]ýfÜÚÀYel¤Êyü+C7c^ J¦Úñvéz7]b²Ð1í9TÆuCBsU,È¢Â,U%+kI¢´m¹ktÚ¢@x3ÆÌqƺËRi¶Yê¨-Kfõ&ç!öTå·aÄ㣲M%ô®s?Ê:À3C¤)õ£Ð?Pý#%;30hÌçæ¼G)æKq<ß!É~~#&s·;Çg¶¸[ï6ws±Ñ7Ò_±Ôr1r¹N+jÆÖ_R¿ðØߦgý¯.Æ4&]ÖíÉy7íJ«VþÊLQåFßüs3Ù8G«õýªFÁ@{L '{?×»OLLÕ·Æ\câ?ðõ[ÆO{×H¹E4I,÷Ü¢¤<#£×+ áÌòÂÂt#×NªÝ׳ÉÎûü¦zKtMuèp|¨åã|üÖ¼'ÑÿX®×Ý#.OéïѺãIÒc>nÿ7%s?®ÇpÁQ'W¼á_/ÓQá÷XÒbeyõ~}y[zúüüÛöâîU~jel¼öóEÚϲðçù%@Q·vïÐÿË(ç>Oiùº¸t¯ëßúwwü>ߺ-£#GfKåôëÛDÎÅò7D÷¶wrWA ×E¦P» Ô´{è<çuÇfßåÙ\JÒÀþa£äÑ2¡¬V¤éíîð4Ñ#zh²K#.ôÓ©¿ïO¬Á}omg+?ÇÔWyHí-9"vHbj'õ5#.:½3̹֫H14¡¦àNýeÐËHÈJHÁÅ Y,?_ñuþ$¼Pÿ¥$./à³L¾Ïöi§30Q]c °Ñ¬²<N·æäÕ÷B¯wj´£ÞkÕ?r¼Cÿ,ý9¹R* °g´íh"jM¾Ås&$T©±*OÊã14_Ù-5,Mìʼö~µ÷#-#.ߨøYþÂâÉlþ¨)ºe1PTÁ8Ð'uÓHX'ìBÅfø_[+-(±E *¨¤´à¨¤_S ±COï»ëê]¬ñr\)@µER(Â/³ÆW;ÞQwhE4Äý¶åz¿Á®¶¿O®¢(ÚËEá-kòL _ùUciDH¡´)ßSZüËÓzUÒ¯R®¦ÞEmð%ÈÅ%!1#%ñ0àüøîr¢ÕD¶ ¶PL¡)"©/Gë7j43?Å6úµìO®N|4OÈóÎÚ«Çmºm)i}:ò×û
)s¸þÒr.ziuaÀ1ÒʳdíÞ#ýÑÎƧ}yÙø²ù§´ò^+l¡:²~³¢Â¶ûîkú«àèp]ಪ+ŹDáédÞJ
S[æܹûïcSÏLü ¦PP#î²u8VD):2ôEÌòRä=<sûR5èÕóL¿YÎEZ¢*²~,wé£#.Ãö´/àUa¢¤¸ y5?zRI(:lÃøT¤pö@t¡ícâÐøÏÛù»*èóîöÏóÝûü¡ý§ÍVñrª£Ê/_£Gn_ ÕùÏÓOñúîçì9æÜWüïFTé_t¸üç´]ã¶?ÏïÓÍAêìëbꨬëþ1s 64x¢æTcKÂÿuÝTðd¹Å8SÂß-9ôcLÿm
I»MöÞAÍÖ¯iVv}½ô¦<W²Uÿå//#.8QîÛ3;×(Us@Ûq7xÏYbz@¾ÚÕê5é÷GAOæ®,P¶MQ#,¶JÏ©5~iSØ£L>{64ààS¼ÉîݨÓ?Á?íï¶|â!ϦÚYF¨vu>xgæ¢æ½õA®ÇC]Æ_B³gO"³I~´NHÛC|kP|þ§o÷Xæßë=Ò!.ÔÔ'qãí"îÑmÈ£ùçì]å$ÚQLhMû9§n®Î)BgOüqÚ°H_®Hlv#vyÚvÉ.K^òG÷{Ǻ§~7ßRÕoäÁ";Ã8tËôxCíèeOö!Þ A¿+$~Ò\QƤ×ÙêÓÉÉ?òaWëDr=çM¡ ,àfé_öF¤Ø&ϵD 'Â{NRî§ÅPi(ûÓ:H}&êKNz5g¸uï/ëøeY{cQª¨Ç¢Q|Eøé)¯¤Äia¢ Æ{é~Å.w&0ªC9Ö+E{+x±Ç¯(KoNÜqA"%/ªÙi~ãæ#-=ù$éñcðÔ¼Í;¸ÛuFÖI$\ü3b,eBr2ÎÆ?[3+â¿,JÐÍ #ùÁ¯äåÖ=qLëªY-¥¥¢Ú#-¤Ù Kù>r0@d¤Zç'¶2$bÍJÄíé*'ø§¥|úöìîþvwÿwl¸½>Ï7ïÅ¿7°ú~`;9iê½w7Ëuz~:ÚÏvÿwö¶ÓÒi#-w@÷h¾¦è}N;4_}$-ÔgqûuíÑv«¨²Ý¿<èï¹ìS?̼÷l¾tXmòÿp'3Ý«¯únë,Чkxì©Ï²3ú¥,ûúÞç ߢ2úO£wÛÃIèÇ×õjóqûɸqæëæÛåÏU< qãü5Âþu4v]áÄÒ_êæðõú³1ËIð¿·õ~7Éáïôgl=í&@«ÔOjüº<6¾kçÕoTfWTMô÷HB¯)ZÞ§o¤õÏaYîþ{üÜoÛ¢Z?d°ÕL·ÇÛ#.g«t7U~<5%Ï[Ç·ñ»~Ë϶_ÒµFºyêiM3Û«sÝÛÚxMͬV®-®~³ä-ô~ÎsÍeþ
M=ý¹S¡ª¡Ê÷K®wº»î¬«-ÛòÐíy+#GG¦ª=PrÑ»tUiô¹aZøÎQ0ùTcÇ>ÿgÎsÚµÔõ/7E¼ûlï¢66{³½+tÁKÇß®ßÛµû9üm,e·7ÈsrvÕî\º'IÍÏU»=SoT]KÓÞÌ¥Þ
Jý¼]2ðÿ®VÓ·ÌsÌÆCïêïjãtô?³õHÙ÷#-¤ÏÖ|yºÿR«æöî>1°ãmÒEå zÍQÕOĽúêó_aL:®Çì<älãÁ³+þ~m3(ZújÉ£©[þ.|H¼)dQÝöì·+¿(ëß¾ÿ|}§ivÝV¯ÍåçáE;tÿ¹ÌYSsà?àúþm¿·F²6}ßYqN]õå"©Î+úÏNüO°¶þj/ù½zj¦egÃCíóü¹Y¾m#Ûùj§>óó§÷{¡:þÎ!ªèq?ÜWè¶0ÏÜ(\õW#-NÏ×fMcdhëÔG úò»2Ù~Êãw£Íý+ú4V²Ür>SoýÝ»ãñü5A´ýxr×t¹ºy¨Fµë£§¸Ù^_¼M°'ZôÞB#-ã£?¯w?ÝGñÇ÷!1êN4Ø EÇd;Q+<qý|îÔ6pg;áäIO?0ûÏâ{a%Aù¥4ß3"ÆkP"T_Ä/ÍÀ3Þð£´çèÓôú¬Þ§·îóýZý]}§Åöý16õ´Ï]1Ö»°ë%ÃídU_KíÏf½:þ¿ËèZPûyϼnôÇWeîˤñãßí«³öôs_/éïýE°ôçî:îmƦ¿MÌß²¶zOéíÜßoèü|ÛëÃè§l[Õ+~ZOqI§Å¿Âî¶[%iìgÏï^;47»ë6Ôy»EBS^ÁgÙi¥&àü~µæj«·t#%}ï!ýqtý´kÚzáIþ!ÙßG×2¯ <úÙ©#p!þMÞr»û¤h¶ÎÎÃirZ¾~ð¥qÛ·ôWÿ#¬^û;_MýUp²¤©wUut²ùÑßü¿nTԣê¯}g£
ÿNÁO3iõÕný'Ô±Íó×fÂÿNhqwbÈ3S²uͨ÷@
·ké¯NfMKÔn tvÞRçÃdò[üE½w8Á#-~öÖW¹>4ðºOªîxjUÑÉ{õÑÍòó*(:®éÊÞlhæ)Þu§l¼2É$£×ö¹7Wrêþþ1ã;ÑqrI'!dåÕ-A¨ÙøoÓ椣-¾°XY^¾ýÝkÔ(Ö Ôæu ýtTCì¦u×Á<(ùvB,aþ[®
ó Mjæ;ó^êÎÙÊÜѦkjÕÂ6èõ#ÎÜ=þN@·y;"'1?â½cV~³éxëÓ£ò¾ÃÞ¿Qû$KHõku~X´1°î;¬$¤É!o¿anrû½µX-ùðknÐ`[^ê涩kÇg71õ÷Àáð¯û»ê¬·ò"ØÄЫ¡ÃBbzym #ÒµÈJÛ!bµk\5ð¨&UQT+mÌyñÛX
,2ú.å6á3§®ªêäVÞØ=Ú´S=·v[e670ñ
Ò}Ü YEÄ"M?ãôé{ÐPa9an?]weWùÆîྷûèªâ:öqøügJÅsÅSm¯wpqþodtùþãáÇÃÍë9¼°ÀG¥~¯^¸mè[cÛüwº*¹'ýß$¾zmæ»»×t>øZËÍþ·}Çoí Âá¸rýQö_g§Ù2ÿY|jJ¤åæI(ób¦±5øâ^¼]ü=8táÜNDªò²¦)e×ì©õ.¾nÿÒM¨gê«÷øWm_à3.dl¹|*äD?¦TVFi`¥pDТ´T©+ S@Vlpȱ¨$¢²BËU±#-d2̳5¤Á¥±QãI±<ÃeM&t!HZÄ*Ûq?l7¤5Fdü¯ú/wd30d1ETbR?S&Ó¬_gYÜÞ³!·PP»ü\ôgÑùÔÆI3ïÓYtø¶µ'Üîw¹ttQD1FneeÐÈøè~VÀàKÑiP"Ö-×"@î42¬Û¤hf#-TV¬i¡¨¤Óµ8<6Xâ¤FõCHyÑvßÈ`ßî¾ÎÚ4g·âQ÷cð:ð>#-éÕ
w表xjÔ´édèÑð¦¾ýµé5ÿ#.èÇV¤¿§ÊÌ}G¡oý?Ñ¢'ï®õtþc®5à߯;Ú¢Üz覯Äî{{«³5ßsÛ]wûg2ÆQõRD©4úÔÑi¡zZð<º~OÕú^V9z9!d!qOç9÷EÒ¬w·îÙºå«WÑijGÝEÜß/.|hâ¶[B6þøY´bñÂ#.c;ü÷KùuwO¾»Íûs¥qá4N=Íò<~îGDîó{ÿEÇöUÑëéþwqéíè«ï®û?u¯^dÂÐBU9öÇ{7#Ëàä'hQÿ ù»NB?ÇÑËjyBm:V9a¤Ò·BÆÒ´pÞ#.êjQ Ó@ÓHÇ£Dk)Õ1z±ÚÞ¯\õEÓ*fÛz»^5ÊfÔÙ6FJíݯ#.òóÀÐ-1N⨪M#q¨Ð£@ ÁBÄJ1BÁJM
JÄÓL%Tt¬µ¥:zb4!äèJö¤ÞáMñM,pU¤Ýp¥JTm¡GtqFɼíN5FQ³ ÁE#.(D0(#-0 µç®é6õë¥,«Ì³lmJUò# È8#.H¥(#-Ã2#-eæ:EÅJDQxS"2`²ÛqKaÁ BÁi*ÂQ¤T#-A;5[a¦ôT&cÈ""¨.
O#WÔ#-»1DÄÒÄÍBªÔÓØD
:à°ÇX`-ÊÔ üBgp¡Å8íÁ¶C¹¬Q²µzµ»xÅ\Ã].4jùU_sT¹RYiôÁ쪸£Ú+Iï¬:ümÿ?ëõvQó7ð7w¶&þ̼äÏÍoãå» û#¿}ÜU]qcSvÈgjéUtSÊxëhoÆϯV8¾ÐkFÎϺç_%öbí¼3u~£¹ôÜ«ág®xF¥C;?®ßKöÌyÎsBF»û8*Þ×Y±ãÑ(.+Û:ÉÝõÝÝê¨jÝÙW>Mî¸åBU˾ç»LHº4Óû¬¢TYO|²g¹úhêÖ8êXÐÔÌ 0ÍQÍ.y¼(µð÷¼.i4ÛÝBLøÛWL,#=í>`ù¸ÒIBï|d,n%ÎÐfÒ ç³\*õ®e¾oï£ÊÇÈÏè)6ýê9<'ñæ}¾Uò,ÃT HV»õN6(àÔJA6PÖúÕ$¡æ\#-Ã.R0¦Q¬Ö0£2N³½zU/ÑvI¸±$ÿUìÕ[p~,t×ÁÐâ¶þiøOÇØÛ(fæpB(;NÌÇfh-è .#%±Aï¿eG'¾MÑ?ϵãØ9Cb©ÇëÄYw{«,M:ÃcyÛúJW^úþËã·t,³]vWnöÒõæ«òÈ/b®âeFÃ_£Ví3WDébmͺJ·4d[7\Õ¡Çc<4òÔ¡-oBÖÕ«Ö¶>¹ÊÏ6ì% B6ÊÄÜKL|<*»ëúÜsrgDº4ø oø%u6ÕD#R"ÄÔKøXOåÍcÙ¤lefq dCXA:aîûèô{&¯U_Gqùý7{gwO×/ªdÛ70<M1W<rJ¿$ÐLA+Úh%Ëc@°ÐÆqfÆØ|j᢬ÔÍ˦#TÕ4DÍ9Y¦9^ºaM°W$Ì,HäÜ´EF#-¢µW½èzMÓ¶mbkîôp\h5^Gl·PvïX,í¼>&Ò#.R:ÊÁ¢Ðà¿ ¤kQ+HL1uÈ&=¢6qby,p#eS/nC"eT#¨ÜÓ´íµ²6)ÒÄì6(XsE!¥NPkSµ)´dämÁÙ4©¬EIp«m¢Ñ£x* f2ûHØ ÂfrhP¿þÏöäuCÇÉZiv*e¬JÊ*êÖ"Ò@|2S¦wÑh2ì7àéÀÙ2§N*+:`ZXFsôçÝòýZ¢3×Øþ}öCôө˳©0!Ò`LqÀ0ФÈ?'Q Ò@èöR9d"ãÑʦ4ò=§³×oJýrû4Ùê³sJ/ÞéR{½©{Q¿Û}víöê?ã¼®ó,&Y*?AmÖëzuî#-5öDbâM&¾%Æ£JV¨'Gk©Á§ HÈ#hf° Ò Ò*#-p)_èí>ÍÞgk6m:;õÂæÁÐÍJcÃâÁª4´h¡El$méÚ,^ÙÕÊ6§~¿yqżtÒÆ"À*kSBɦòþM}ÿâëçÆ~ú¼gö_E<ý³ÓÛãàiÿÓýÞ5ðêE¢cMÉÛ (6ñeã4.ZP)MÃñÖë4{[Ìø¨æTSÓ§5ɧÆêFè27bR+BPP¥°
ÃFâ«IÈDB2W_q!aêÊXäiÀ¥àhײ¥ãZÞæt0áF#T23 îòL#%X jc`°ÍB6Õ¦^U@Q.ªX>[ÑèÒ2®eoó8w»Ì]M¯nwÁ#-r2»èÐBdÃ4pa¾*ð¾)2g6-2ØÅàqÄ˧諳Aùñ(°Þ5FGbvHÇczþÄÞ³úð¥Cµ¾4ZÚ'>2§@mzàß·F¶!<ëKa%#.ÈÿXUãWj¶¹¹YäU:b¢7)5su¬Óvøh ÿgó~£ßóâAЩʼoI¶wõ_¿#-ÑMhSspåv²3°Ç«#.àð¼0Ô¿8 I;lgèÚ3zÿÖ3Þp±py\/´Áµj&ØÂ,¸ãL`xËM±µ,.8ÛÎV>°m6ÆÕ¹1ZW¹e`Á¶F#.©U4âLe5HÅ%vf¦WIS©{Ýü5Üd¬='~3J g´oÓzh²-a4«%2¤U©Ë®¤Ó×F¸]$ÆúÿtI0Pi@ðnÕi¶#-ðßT§ØÖñ¶\ØCP-l7
·#-·c´B£Ë'%Á̸i¨É\W¼oÖÛmßðZB%TfèíæMæÂð®)ØgSÓ?6(!ÞËcs¡¶ÆÐ4&µzYBùq±X6káó·¼Û@èI®ßÇÑèpEÓS*Ù,§i#.ãj«¨1]Ó51E¾áã
Adìºá·anoö[ð;ípͼ´%1¡7VÖzW1Æv#-Ç{rNF:ÔuÖ¦[ïµ9XO ¢+¸®2YÌåº1&Û4®pE6kv$<g:b¡Çûq¬Û8Mö$Yª[¥#r5A±,`îõ²6*m2,}N6qq|¹%¸µr`*¸¶|,(#-5d ÜèÎLÒb0DHõÓÇHÆóK¯BÅeqÓoîØâ (~¢Ñ'cöQ] §_ÍætQ(þq¦Ô©ÌB#»Âô¶zñ?7Ís÷L·sàå½Q³÷l*ÐõÎåSbùËvÖ,¿íqô>ÏëÜ¢ ÆgKý1]ªç~ñú!ÐY£:®6\5<ïÍK-ê?GMæL1ïvúµ]ë#.mæ$¹>g3l¨·ë©ÖròMNvlY)#.¤aèR`<óÛôÒ×ÕJnk#-o}&óÖ&ÏíÆLÍ}u<zÓÚ©]ynQ¼`Ä&8ß"èË·ûë®_Ä<_i~ý¦µÙßÔóv-ñ¼MN9/ºÎ3-RðµVË\dóäßק'¬¥-8ÌðuÕ`Ñ,¢U§o²Úðô|#8ݾT,³Ë$ýCë26ypïd(´º=²>ÔS$u4{_;iëoY¡ä¸O+rÉZ³#-u¹¦i]$øQ$Q9uäk#.#-Ö%ªhëÓÓ0ïft©)7·ßt\âó»±voV)fÌèLfø£JÍNÎÍ-;iã#æÀ+H$çr Âìöfávvù4;Ƕ]\Ôw/;í3·xvMÅw[«yÜyÏçæ8ÿJifË7Nx#.h4Á:L&uí,o2J'ô3¡U1èCÀs£ÿWâ9îÑ.®ø´qÊÂMçöü¤jýjÏN Bð*źK8>²k¢8íöN$yH·£#-TéÊúÉ1WcãIÎ#.#-ìûºhÏ>³XÃwjÚ6(Kêöv¨QwojóN» H?w±ã²/ç^^ói
ó¡aé(ÊVk¥dÖÝiT!ÊnõP>#-A)E´úï ñò¡³¾Uºu[Ç6)U¿ ,21Gñí%SÁy0îòò,²±$b'gÏ'J#-LQáGð¾ëK?Ã=eµÐqÃQ¼Û¢Jm´rnjãÜi¨®PìØTy4©93§¬!+z%~#-F7®B;.þѳ;à0#-=_²eµºt¿?¤»¹îcѲùï¿zqÞurÕUqî§? CIA·çÔs,ò¹%ÙÄnA1$RJ2®ÇÍ5Ô05Y°Þ©Qg%°Õ>Vü¢!/×Ùg1TùAºø²8lþÌð6[Oi+l#vr9© AxU
-°ºsÂáLl¡¡¬;£p ·\g´÷dàøàìy¾Ú}ßÍß LðD¢ð&Õ_/@ð'|'ò5~Ïu<QQ×V¥)¶DdRrB{¦7Ù^;\y¸Jfu¸CõÉRïàç#-ûYy ##-á7]J(¿9ÝRO¨´ÊÝm ZowïèÕøf-úæ
ÏÜü·£g´¤ëR
~Â>ÌÂöøçHÑzçjV µ:0íÛÅÒ~^[ɫ£ÓÅ2`èAzß·¥kË<=².vâgÑ#-%dð-ÕãÚê#.R¬wRi.ÙI,M¥¦£Xå
µ_/a~D0iIAü
|zG#-/fúu>_¼;á3#.\¶l¦1ËfVÉ?Ë=2RäämFÌ;îtpÜYòû¡dWSÛø÷ëPêéþr|üüCÍ->|#-#-u´À±Ø~lôC$×áëìò«$=¬,K0ã³¢ÝcÜ£:Ø úUFç{azo£^Ë&³ØF¼òôجÃ$3aï4ÝZÒ>tIdÚ-0ètáöm[øÛß³{{õ½#.`%·®·ÃfØ¿÷QàªÓdù´[}J²³å¥v^BËTSÊocú9*Åû\C{*ay+÷Ê2ÿo£o+²Õcn//"Jos ¸æm¶g>ÁÂül~.#-9uùoÏeÀÏîu¥6ä·¾×8¼yÃGa¯ÃxÛD»U.ÍãîCîvú¤k×Ôk7,sØI6ðyJظß+r`qÉAtÈÏb£øÈùqä`
ømT(2¥@ÑIPå%¹giõöì&Ãí¹wW?²jæýQ'ë/Ýñs4Æ A¥BUl;³ÎQ4j(óëzýe=u<ÎÃnda¬ÍìÓáÚ×Q4;ÐNéøí¨3{bMàÅï?ì^[øõÑmwÂà¢nÏÑ*i¶CÖ¥®×)\ÆúÝÛ¼ÔFûË¡ÏÞoÎoÈ°ÊW'ÝuùBc÷f¨Ù÷n=ú=}ØסÄlÙÚ²Ó|ÜC<¼g®RÆ6}a²cà BÞÝ®©u{n=fìÃêIÀå2G?(l»`_;}hdPN-1MDf7T._ßâo5Õ÷ÞcîºpËì{¤%Ó/ßmãù秥äùm¼úk²çwÅ&6{z@âïy©ù7Á4óïç¡N½:IÀa yÉ°ögÞ#-§v¾ä¾6T·zª3²8m»ÄKwèÆV¦¬Ù$ðÞ'ÕT¶6ë{ì~îÙÖµ×Cå£80±Mºè®BÚ~Ü5M>OrncÊöiE£Cû£È¢Y´²TOV¼ÊµÎ¡^'ÑVjR!ïÍ<\lýoÊËÙÜE$˦¬gõ/HàÓÅ9ÏkßKÕîß5þ)jÖ=LõE¶ÏöMJ#.|ÛïEáa(7¹Ç«;[IvÉFÍZ|½%ÇN±pktà(Ïki33FGÑ
P¼1Mè¤ÂXá©c²®&^ÊàdMÍZõnÜ?D#§Bð´á÷g_WÉÓºwOæcøgå´½\âÎ i@Úo©Gí0#÷CäþXñç½yñ/ÇLç w+ᥦç|¥Ö4(hÝ®ÈÑq·lùÞì |z¹Êê\Éâ}Cϼg~,h6M¬fØÁhøÆÞÍ·ÞkbÍ sqÓ-å#.ê
¸ éIè¡ÇT´vvlSG&Ïy$ì!ñÃM$¤uçi äêuå-vqVaé¢4í%OÀßØ~36ði<µ®¸Úµ×;øf`:Mc\ûÝÌÓ{T9àí0+##. À®»£Ì²Ø=Ðëll¿´³þ_ßJ÷ ÷éá÷ðnD³Z2»¾<<Ô¶:ùØfúò;®yÄkÄoði+>^Úö4k=øF,Òr3N4¯rz»:jD¼F}tø,ïÁµå#% °~Äâ;Ü]Ú©ÏlÆsx¾7ânx=âÍ
4µ ¹«Et|0ÑhF\ìM*t<VÔÚü§Çgñs¤æ¡Êw*ÿ³?«#L¦SÕÛû§ðSÔãUøä[tlçòþR)Cn¼qVW^h½"/lÝÒú.¢Úh#-OÐÈnÁähÊÓMkEö5¶;Ð,I<'k/NHø@»UÐyhÍ·§QÓý²"p¶2ç²>:/²xèíÉElé'}¹ý^eP®IݱÄYÕߢÉè<bKöõ'µÖ«ÁÇ#.Åôþ°jòÖCé{ø;å!*7af6µ"¦¸ß©¿l¸U;T6+ÏHÃEu«F§·
èÓuW?gkxØf¤Ú{ìý²õ"nÞæþËéåNW?Â.WxîæOz¤Fý/¢èÛ6$õå"RIÅìMEÕuWJýÖáeèÝÜåuå5¾ 6#¬õ×/¼>ÿù¡Ýv5O}ºñÑ%Î{ùùø¯Uã²yâÎJò¾ÈèÚ;vÝСóD~Ȫþô=3æ1&Û¨3æ8õ¿$w0&ã¬Ë?eWcÓn¼m5HçGôÓcXíÍ÷ޥΠ$鿶uÉ}J5²åuöþ²Í7éÇ}4§ë¥>nÑ¢g%+7öQºtÏeykḐDv¹¹fïV[núÀåpZyêätúR¿SöùçË©ã¿å¸$¥ñ{ ÆßãìffxoEïö>ÕÄzDn½Îþ~( VÑ#.Dߣ®'î_K.v<O§Öqî5±JªøåOIw"ËùٽبëÛ~ÛþKx¨Ôp,/±v
8Q²{ꥥ¾ô}ðl Sm0¹Ñ««+IV(>ÔRªòÓêO?&ã:[EIi:ÍsR¯Ï¡eÏ´óy«]ÞxUÖpÛ{zxÇßUÌ3ü·éЬã·ßYÊ!F.Ä{»cNÏÝÚræL¶#n\Ñ̺8]6è¶ÜâË8ëhy?C§õ¢v)ØüÍþøÆfäûë®øWò9ÏùýÍ|èu§Æ}#õòòÞqê>ÛF·U¹Ô^/»aÞ#å#-Bf#eÔNÎ)í,ªtgÃmu.×¹>h´Ñß8©:¡öHµ9]å_¶)à¬ý¢ñ¿Ç_µßíǬV÷îп_hi> õ´²>çÉ¡kS+7îé½w#-W~ÛIiy>6ø^Ó¥hÚ¿w_>iô<ùóåuæoP¨M˱x<)r~Ø6]ÚÖm.xíò]bïQÃíë{jHÚ=ãü³{¦ìPÏ#.Õ°
|.¦:lr¦KBkʬ=60ª¤:ôß#¦vYyCQ¡ýkRc8VÊ:J6B:9ê¾~}¸\ÕÆiS½Ñý¯ÙVå;4B뮧}uaÇO\«z,² Ð[lVù¸h¡áE[é8ѶÝK\¨+#-Ã|§V#t4)SEjøjhg'+ÕǪê/NT¨Èüo}keí×uû3¢Ñ½pKm¢èµèKÏ)Ìñ¸W~OÆ}ÅÆE](ßgë³écïN~MÏ|ïýݨ¿Z7,yçG|Í6@{uYõ]»ÇEçùÇ~˨ólºxW¢GÏÏ>-b2îz+¦ ´.!¬¾øò¦ÁrAKÕi#%¥ ¤L&EH%J±*k*x£UÆ¢íÛ3Fü6ƽ¸ÞN3î\4VçÊ5ßÒ;rk~ßH£ç´f³I¦xÃÅûä<HÚñبÐñ,ÓÉà´\=¡<Õ¸só«5U%ЯB±K®«ãeF¥©¡avwYM<ßIݱ/oÓ¾O@©>ûyF«Ö¦NÙ Õt½õåìuþ|ö³¦3ôcu8Ï>¹§î£Ïòø[{|Tg1²=à³{ÔÝx_VÑÄæG4Á[ý ñ§rE÷#¢?7òëY0É:G$ݼ߯ÏÎ(ÿëó»QÉmU¹?Kº'
]é`ÒúLl]¥í$%°èGá´Uü¾û¼ü1Ò¡Ì÷DX¼ísa,÷×+lÐÎôE&Bcµ¾ÄÓus)²ÆºØUKý\6Á®5qï}\ùîzm¨9øwù|ϯ;¯CãÕ5-¼Ì"=î<<!lÑD§#.*a±ÌÉ/"zæ\®ñ&8űûJsþÞÒÓë[FÑ«9T2kzÞYåÁqa×r#.My<¶Aëe]yï0Êi/é°Áå\ä.ÙÒ^^>y¤ËÜòÄ%NÁñ}pQBµéÆ#.¤GÍæ:Eb]Ú0¥;ºÞXí}Ô]¨}»bm˯]í®#-eÙ¨sß8çÛ\#./£BÛñe¼£èI&Þ¶IêÞÿ:gÂè"ÂÅëÇ|FÕ<*HÑQå92iÁÆÔ#-å¾_vÇ«óí٠ǦµÙ×QUnÕÀ~ºÎYJ-¥ï;%£=uçíI^E:y}W×ÔØùý>]=õÕq½[kçF¸QI¶©ÿ;%e'±ÖélV¦ü<¢-c¯¦_\æ(>_¨Õo×ûhz¿ûSfòéüb×==mÆðåïdúãI,׻бþ¡
õÿ3k|ùÝÛiÜ2ƺ±35s~ºfÜÈøÍØð«º;mpS8Cy:;ºÙ8Urå}Ðs>¸÷ÆPÓ»ÿ*RÓïñ=Ú4 )yÜv
³ð°º·v6¿ êË0§ÇDøpz>íë^ôj½5ºa§ýjÒÒ×ÀA³ôô¾ÿxÑùd6;ö@óvHdTgCóÆú³²FÜÿ_§òécø×<-«WöY-³¦èC§/ ëΦbèð~74¼ü2¨²v ¦+Â*x#ßÌñ¶bÜûHÏÐ[tm<üsÌb&>Ïã´oXÉõû»×[ô6¦µãwl³#áÉ?yä¤Û/Ez¬ÈUNÉ>#-®©`êuÚlÓ¢o(Nßa,Vè'=£Åù7'O×®ÛäÛn\CpWÝ+¶q¿4}UÅqIñÖ¸¦¶Xüë*ö_ío7^~}1£UÂx:³5¸·®#-¾×Uüð
sW,¹¢ç
qôùª ºÒª};8½qÚo¬4ÚÔÿ¬dç0?ÁtçwÞ\áß×_5Íg1¶n÷å1ü»}hç¦Ðou÷Èkûq¾¶b0qé~¦6Ù7Õ_²R(Gym²m ±vá?Oãâm´ÈO¦Ò|v²2í¤¾? STUFP"(;hF¢qâY«#.VÕ¢¡éäµD©5Bdȸ\|0 ¾¤k²z¾.ÃVùÈ@ú®7*þ¹/©Â#-ؾ2JVµeAÒµÀµ`¢7^ªüt÷RwUUÜ4}þPÓÆ;Êh
Ȳ÷Ù:øQá+[mÃ@¼(ºã"B;)»*éíÙWFÎÊlz4Ò]it5vøq²ÔFðæ)#-ê:äSÏPQÊ{µ[B§¿Ióý8=b#-öu²^ëù;ãc³#-2SÓ³à}³Fß@ûIgÁãNïËü´$ÆcciúñKçç¼[eáòú\p&çYÌé66Ñëïº^x"zªûAY_jùvã§#-¾Ï¤Ä¢c¬½þWßB&P~Ѫ¥UgÃè0÷äáÒÎDiss´º4¡60øfµ¹íâ?~ÊNxñÖ6FyíÞI!
Õê¼ä0NÅÚ§²Þ¶¯´ßi¼tß1îÛPfãßÜz]¾ù']äl±'âL/?gÍ3cË&£cÙð°dî¢<x%s|fµ-Ñ:ðåTûÒ}Ó±Ùè(|£²î¡ã~l_U«Ü±ºØ6¥²¬"]-1³#-xçKïùÉâ/UÍìD^s<25Óæ¥Ú:³Üô¬L£C³Gðüq'»bäKw1uæ6Û]k8zÜùK2c8ôu=¦8Ý×®)B6kÁ°la"ÝÑy~ºØu¤EÐÓªØGFKCÃÊøå÷µy=ø0êï*÷Hú#ª´^?#ªcÁ:}×Xôå²ÕìµÖ§©KÖå§w5>ô¾ª<÷ÐLâZ[KR¯O\~GÃ&ø9ǽ9RkR7¸å¨«¢ÈÝes5º*º#.0GD¦+§¿6Öý<Ucaór%Îo ¸V:pt:+7û%ÌNÚxIÄa@ð¨^í»ÊCòçµW8cÉ3øs{ÎLîò6Á¹3²Gõ¿®N» ?7íópé¦q+Aã#-UáÒf!<K
EÉÛ1:Kg}éÜÛ!îl#-Û#%ê9 ·JZáÞ¹P½/h8Îé²Uפk_vØó·ÛÍ?öt£V«YÚ}°ou6ÚÕ«A^TDiQwæÅ]¤íN×tSô"¢àun;#.dKÈ<_Õã»,NxÍ=·ÖYÉo=qò3Ò·a°¿»YÎ:[ ë¾é8`Ó«$e®|rs~äT)$ÏuóÕeÛ|_ãê_ËéørÕÛ@ã"I Çoøï·w^ áðÿ9o,cqeVWÔ¦Óxaf#.PáíçHÙðóCµ°î¤BVPÏ+n¬·µRÑ¥¶]5]ìwn$Ñïõa3K}fýÓòö¯´ÔrÄ4Ááv$$G%;Wo7|pcظ¼¡Zþ\p t×$NÎíGFÏ2(Yßi|ºÚQ×_UGiüÊäVÓJ§°N\%cÛnòO|9rïWºôtÆ1ºdg
=P¹Iò|S^n'íÄõ«üü[PÍWîéú?#.½ÏÔéBQà·tÔIÒbuàgêj¢¡'+_=|Ðͬ×õµP®Ë¯'>Ú÷NÉóԡ Ùü,<È<´ ´çïõ?Cò¶9Øþ¾gêM AÅ~¸~OM¶xy4¯¦]æÙ«c3E©#-êGï?? »¸´%=¶Þ¸ìøUU5
ÈJbÅ&³»;êÒM¹HgLÿÆ`Kj
JÂèd¿¼ë>ÿðÁ*E
_¹µü5`ÚKhÕbÕuÁIÓHs2¦ Äép#%¯üx¹î¸bÆ|ðtæl_®pÐö9Æ0"$`¤V0X"° lÑÓЬv¦Á«)¸dããÞ7~îc"¹ê¥Ú°ñRPû®¹
$µT í!¤ûcÀýXMî'°'éiz×1ù¾}ÙôÓKL*¯ñ9L¦AEÝ;«ø÷öÚ7åå|Ü©Íßrÿ#q¤¯k^tÿ[0ãsBÞSj!ò3¯º²p¦2¤¯Akqõ¯qµ]%ý?'Ç]"¶Xô¨sC|3ÞsâÆ Ó½øyq+¼ùÁðÕÈ%¥þó¿à} o5ºâRfÛÑ¢Ð[[SQµòÝI¿®¤× 9¿ÚbKþËqL¦|þðþ2!cäOÜØ0ãÛM2÷iN°5 ÷#>ÆΪày³e ïöb®C#ë"IMO´Îc³·Ã.ÎExÞ¤#.eA{#ÏqÄi[·w1+#-|Ǹ}Oöí÷ýé<a!E'çËæFØÓd[{ö3>åìßDV5¼9ë*ÉǾÈ+|exøv/Ë~JOÝ<rcïìyEÐs ¤þ.§ÚiÎ#c(QñuÄÒ#íqâýpõÅ4ÉPNë{¬ÿôÖ±¨{È"Aá¥#.4º49B¾.ÂÚD)ES,àAͧ«+%tÜ"#-ñE ÞZOaÒ+í
w 4hô@<óu#÷D2[áÃÏ°ÃmóTÌÈ@p/§ûz¬T2ªë^P<*û\ÍõMèZ]=ZKE8×Á·Ó¾ìïOºo©¶»\Ðd¢¢1®{ÚeÅÛÄÞ´Ö&¶§f(õÏ(¤PÑØ(
WQ½RËWE<üNÞ6b@]Jßb8w5%½VÎñÏîó»Þwð¼0Òp`ì)üÒßXö?ÝTáD4Lh~Ô¥Ý!oÁÝC`HÜmåÇ¢gGCOU5I7f>XÂ9ïL@ÉnD7}£ôÙQîé¶tæÞöÕ0ö)`R®ß®STÆ68¡õÜïguJ¸<ÈyúÔÈâê9$êhöçQãn=x3MÅõef©±P÷»t δVx\LÊ5dÈÁH#-¤I!æ.c¶>ÐQªÐ=Ä·£¾sp$CÒ§£B,óç}ÞsùMÚ_^U;9F&cXFlA`θSa3²¿déÆî;ö8Wt¬¤õ°3êÔgÛNsSuuÙ¨Z-ß»× Ç¬d³sÖÖÜéZ®äüÝøÑðãÛ|%¾¢é<¥<ÐPV»h^H*AIÊSn0,!cd#.¶Öh,Ztès³ÈþÌpFzg;}q&}`!¢¶ú.µCMxüf¶0QÓHÄS,jMÝôÚ¾þuÛãzødÇ?#B<CFBx°¤Rh1FIÄI<ÅYkRIf1 T>põaîêÒÚçÎïåÕÒ9ØÓq²C;ª&·µ$6v¸NL®Þ0&zóÕQ/&>tZÔæ÷Ð £ðÌC- äVtÏnuQèqÒΩM²/-²´¾#-á²4)Pêç°c¬Ïnxb ¹ôa¦6 }é9>¹ðζ;k¶ 5qJ!¿<h{¶R\ªöRçùwyXYBMÉ¢\<ÍqÒ#.à¦cuÛ¹zV+ÍgãÆW,1ª»ß¬¢ä]¦ÔN ÉêL:üʯfÓ]R+3HNz{Ø1ÍFø¬·i}ª¶n@}4öúñÃÃfr(Ån¸¥kñqª<«¡ùÄóÚ¦Ês«C6ÎÙa-~yônC"-#.¬!Ìuß:UØPNIaKkX)gtÑ#-Ma#-¸^Ìî{EÎÏG^ÙÌëCQ; >wÈ¡ì¾ë?zó}`ûsnNÎÇf#>÷g)à÷¯¯«'ÕVM`èqd#-ÐàL;p=ó*Ã'iPÔûïË2ÑVAÛsߺøÚ±¸Ó2ÄÒiß>¨b<4[ëf$yHÒµÈpÛ,Ñ¡(»çÓeû¦uWi¤ÐgjV«#%v%³rw4ÒHµ&\#|2x¹ö
mÂñX?ί÷#.5¾f%É^ïÌ|Ê:éHhÏ×¢y+Tñ¿¾Gl4{d¢ÓÄ!ýÇg-5â5ì¤JÁ:îs¿ÐÁ×G2ãGÉÎDna#%½ã o!0v¼±Þ¢iC#%/E#.&îê²!¤z9Ý#-¾äÔÀ^rhÚ-ßEZútG;Ý;ÎÇj+avTáÀ F ²2`¶Û³ææÍ»Ë,¶¶!{õêÀÎë¶bÒÙàQÓýúô±¬f`Ù<5]à ̤ð0$¡µ9qM¶<ÛÑ=Ѥ(È¢%âtúi2ÕõêY:âT{ùW#-P×îÔyC¸H¸ÒöNîÖAuóEèKÀé4Ôé#.5a'ÎýI¨[3
J#- #%²ª«Ù^ ôëÊJË s~ø«cçõ6böB9ÅË"ÉÝæ½y(e#êE B\miÝu\Bíägoeº;51£sL¹[üóG±ùahì³õ3nÔËH㯵ٵ[ï±½!Û*ÛU8ç×:KÕ7iªÿ4?ÖÌìvÂfHh;ã9¸:ðy[ôãsof8Øݨ0§HQkÅ
ëU7W¿
)/¿\ýâ»Ë%qãÓo^ùÑÞÅqjbå
Ï#-oCVÍÕô.Ýß[jî@ÄìÍ>aíã§!˸"#.oyéå#-[ÞFxÔ3.éå#..m[Æë!¾ê¦éfãÖÐ#¼ Pß=1¦µJ,[#.m:s×åâ+åuã\#-Xe3PÇLþ*ø¦Û×~1¯rÊå[ØÔlpBhÄy®ÅH¢î(¶`õÙ"tUu¶n§Ôo_õC¤ÚréǺT?×þ
²¿ÔJ¥Ei·nëígn·^Nj¸ÇíûûQhwNHú#.=Fîq¹8ªè2$zxF$"%yý~5|I j#-¡ýävnÖZùbsMa òëãÌÒMýPpMÓ¾Ú8LBIDL ÿ(ð®ÿ9÷ýíÃÂöz}q9ú^QÆSmÍØbÉM0@´v¡º==¯ßí¶$ôX×Àksv¡Xô0¨ ßgI»¨²#.¤½ðçD¸Øøñõ¹±ÁË71ßìŧÏ^¢*[áí,y¥Ã¢E»I~TVóÃe]ÜÔÙ#ÐQ)˽»³f:R¤0ÍOY#.Ü*ó·øSi
¶6 ³X;R6é3jV·n.aÊèX»¬¨[Ìz8pI#-}9¾A@c~<))9üÜa.Úé(0_rhûÏ?鮿R#.´#.Õ°êO=í0tÚEìÙþóÇ|}i&ÑévèÕBµ©aâ
Í"¹·Ê¿Ãt´SMüyýñ<Z·Cf§ø?´6¤{{½Ó{D8çÀ)3Èô VM@Fè
zQ&hä
{eÙ_kMðQwÔëR& gDU4#%êXK?®KpÍ)(É`@\¡@©4&¾æ ä RimMlÒ6EдÚ.ás1ö!¦[´d#.Ø2HÒ)t&M#-Ê)¸)A):®B JnF¦]i/o¸¿Çg»|ãÉÀÔèqÉ$ù#.0xX4æ ì½;sÉúj!綻'Wômóþ¦Mø)qt¡ÆÁrÇnóÕ÷%"Bok ¿®MZ.Z58 Ei½ÚO+±Âré<*¹ÌÍT¾¦¡GboµAé°²å'=ëBº£?ÑoÍQÍlôE'þ~*t=-fÉ#.SýäC¢bj%H±e Q*¬AbÝ@îKx$Âo4÷\°AfÔÛj9Ôîz÷d×Àþr§J«BçõH<Øc`°¡ð<jØ!]D³\Õ¨åq,¡7(Ë©5Ю ©råðeÕCvNñ©®nÒïty2âÓøz·Àw Ìãw¿5¹¼[òË<"ÝÝÃúÐÏÄ5uà©a¶¹E°¦R{\$my¶êÙóÞ§öúûöé8>`RCV(iV2Å
Ém²ÙM7Þy↓(M5EJF)(ª
÷ºùöî4¤ÁÖðæÍÖhõº©»ZÈÞR:èÞÊíY$`5@)$dTÝ*©)Q<FÊ2û â%¦ï/?á8Ùó`uÖy]bE "Hwñ9Ó/$5ôã#%0jÉÖÀ!ÆBØn %³eÇÇs|ê+´é#.Bé#-ðQ´:{e©£äP[iª6)°ôÀZájܲ#-Q[Ò9X1Ãsälå#.æ}´¨d!åÏ`èÃZè2¸ö:³S"6bÂ,ÀÖÊ©Bf-h×m¶U>Úc[ub¡ÖwFhé&9îJîÍë¯pÊ|kf|ØäÀðE$< &ù5à¯æÖM¶Z5µ$i(1#%jÂ~GdÌHmkî|õÊ-²l"H¾¡1&:w$'#.eX.£¿KÉ5£ÉÏNîÑÓJZäʼ»0p#)ïétÇÕ ï6ª}¶iÕG¡«uöRo/Ðúúì3Â,*ÈÅ(Ä¿1"|KÎù*2"qìÞ&Û:à!Aì<5P³ÚÈ¥Xº*Â&ʤpÀ4¡#)禣"3g/ìU`Û¡¶(çNr÷`®\8Éq¥Õ¹¸V£d´mrñÛ_cúPÒ(f@$A®ûzM¡WÛ²{l6QÁå¼#-ûâE<8UÅÃ*Ü9VÁ8%O!DÑlÏeåácÒTóècNÛy_Îb íÉ!ÝJ²
d&#%°IÍRl.G4áß·'«>3ÎÂWMáz-ø²ù5Su¤( é!(óys¿=Ü´^ãhªiÔáÁõj ÌÎ[D/7Õ#.#-u[Lp;%(Ad1!)öQ°êãG1mé°©Q^=øÊbÄÂÎÊÇE4,5 onÒÌk36#.¹¥{íU+K8v@v+BTéÆòÑ`Êwã¼ñ/[er».×y7:M±(XK¦á¯8ÝTø`ÔÁV©:fÎ7NàÍ°KÄ£¶/¬ïïO#ÖPtli{YÎÐ#.FÔ©¾´DÓEA"wçÇÈ)׳ÑPÍrê8ÐT5(y¶ùjîÜkèÑo®Ð²Í7ù^»6>#%©Rywí#-ÿ$!!&A
#-6Ü¢1ħ,¿H»"æfæçÐÚ]7>X4!Ú·»®ø:èÕ[VÀY²¨VD<klÝT¼í£¸s<uï¯qÃß|xð¤¡Ç´ð6ëø;
=¦ò.rØR£^C0ýoNY'Y¡DØñîïÖçÍÈu5J;g\ÛËeû
ªs»c9ÇwîÛÆÍ£d0´«¥ö<DyÕÆ Ò;î^sÊBl&h·gÛ±½ÆæÂ3§g8¦îá=Ú0¤gÃG#-Æ6´ì¿áÅÛ!ãa ÑÈY[GA{¸bûÐPI¢ê©¶¼xµÕçäUªå$ébT6yݴѤµ°X¬ÉÎh§ª)Ú8A!gê¦ò»dyüð½Îl÷ÃJÔ<8UBwôVÂubÁTôì©¥É QFiÐè"`!2#Úpæõ׿GíÚ¸}yã¬íSbaþ__»%V2|>¨KU·åÏ~µïÜoÂ.{Â:Ò¯wSã£ôtòZëQCBµANÃiÙ#-¯ÓG¾º©#-'#%z,ª>tY¸L6x+Ü«êÍv£#-ò}ü ePMÅ®#ÌïÉÒ«Ö¹·âHJÄì¿sëQt¾Ù¦üíiySÎ ¢UÔÔ¨ÇbIHH-0(Õb1fZtÎ=+Z Ý4éµøÛEÙÃâQ)]T(5)8±¬%yk¸¦-Hb/ï·¿ïW@øÔÖ½1l=;»¤D0Á{Òìÿ¼ÅW¹:&1Èh|" z'}m*HjRd`;rY~ÒÓ
KÇ]3 ò=UÈ}¾¹ócþÇÊ¢9ÚÂAÕÛ®
y¾GæDuÜ·¿Äf ûÒKJmËÏÍ(K8ÌsMHéºõ&ðÖB·O9ÂÍÐÒ¢
zËõ ÅrfeÑy{ÄI¼ÍÝÕù¦v] QYõ¦ÎÊCiN«v÷!Ê9Ö°´t?#ßÊç2&ð±Ñ*éöa¢æCwxô#`¼ ×ßFq:&^µ#°û:µÛʺL[$;æ«Å¤SKÓ]TbDD©#-Ed¬L;¶*QTºd½FÄÐñ©¸$C¨E¤7#%lbÆ×\aº§V8¶VÎ}ûtÈ(&Ï6ýxP´»LWtr¡'â7Þ2jFX¡pö8ù_Yxò#%1ðñ¢¶î!{y±¦Ñ Sû°B°lÇcÈ1=|ÒÒ:&Ô~ç ÛµQ&ðLYIHb
zpÕÙUí²ÉR3Y_Pµ· '×ËV_ ¯å}?lMéæ;ûlEF9ÈÉÉéýzÄùÃÏ÷ÿtè¨?</¯ùâóª|Nê_(Å.=TU¼¸*gT9gbÓ©Ñ[8Ñ?7&øp¹wÛL}Q><xÌÏçT1;LÝ0þ3ëå<ÛÚë#nùV=ñ¦íµ½%ßïÊv?BI³b½°:¦ýH¼Ra#oVåPL#%&P(ÕT8ú~ÎÊ©ööý&)#%WõQ7L%$ÁVͲÓQýÏ>^Ûlª=1åº~]ÉEg,ý±ÝûR¢ëÆò7té:nH$>§ës!¥7óyàøÎ¥ËdS#[×(-þsú þ ]½ºk}±#%W2zþÓû¿¹ÿËþô¹þl'õHöïÿ<DUYz²-C=FkUóU'ñçòvç·n/3%ýî¸^Ûï}_yøÃùí¤4G·v¦$}Jñïü °··lè>3àQáîxvt¤ÒMä $ìãÜÃö}¾ÿ=u.ÊX8¦ßº¯ø÷ü¼~]{ÎÕ×ÙjÉÞ¼¹ô¿×Ük·ó.WK&Ìø²ýÜËRâÒüòÏäþ{ÇïýFÚâ¨ýÎü=þû1¯OÔ¿*)')}Úà.B©Ûª»ÁqÓD8ÝîK9K#.ì,«/jÈÝgcU1ëf*ÑÔÓoÒ»[húÂKXÆrÕÏ×;´ì¸Ñþßz«¡ÈÚ¯U7VSOk}Têff¶ÿ&f`¼xÛ¾ÓàîÒÉh(W^,ÕB(H.ï[éëµÈqÆÝJ©ûÝÃ{Üq$¼õ(T=1IG²W>8#%2ÈtQI$
¹¶<þ}µSÒQTÝc.8|ïhAh¦#.D##%)(rGWêÞÿ/ï#¼'IR\gÛøóÛbÜf!ÅÆØÜhî6ÏÏ!~Ùö¼/¾¯jõ2ÕHbÀÄåÞ_Ñ7½y-)ËjºlÔVïR¦5zàRýå4D«ÓÛØ\~CeÞn>¼ÂÌÙæÐ 3mÈä¤|àeºR:º8ï<Ü=¯èííî+Ehæf` ±åß/úZd û(N¢Ó&f`Î/Ë=agégêé¾vËï½ÿ¤ÆÄ9ÅJ´f¸ZÄÎ×ù>n¿'xóóMTºBc÷ÝÇpN:úùÀéÃw»u?9P<1ë%Dnëñû>â,hîK½üHD#.àb(ùü[öf³Gçd6lO\{vaHàEO·v|ý[:7¾GB#-ö¼´äz.üõÛÝ]5'ÜHÑî
¸Ùß»ÛØÇiú??nÇøæOÝPüÚµ×æ²~I:;á)·æÁÃÞõ)D$0]Ýò|Óñ¤ÇÏÓÉz9áËÏ'¸¾¨ÑiÏI¾sçg¾t*mvf`¢dQO¯Ývû<ùí:½Ñ/,HpsÓGåÇFÓÂ/´f`T¥(.,¡(a©5ÎÎÁó¡µër,èþ}²ì7/
>Ë~g:é8¹çϧg$¡L{ú2ãÊþu×GGtTçôÀËÚå2QlÖLƨ/»ú0Î!3xkIi«÷:κåé©g×Ó]çCéÓ¾yôwjÒƪ#-bdÈXÃ7¸,×ÃZÔó×9N4Bj|I+ ²I7³·2nøLIá<yWmý\)ß>ÎW&+aï¥C:QÍ ËJëÝ_^6xÄDwÊW2?ñºúæk4îr§9©5HÛæ¹ð̾ßó6&dIABF 'G¡Q:që½æú&Ñ3ò¼må°®+Ï=Ä ÑuÛqff%<(OT!SÝ{D»OYèù9§3Û3ºÏ{`ÌÁêÞÝ{3ׯ^2ÝsÈ}˪EqªIâFÌÁ#.;m,nªpú¬ô¨Öõ¹]17wNM\¯h5û&¿wÙ¹ÎúÜ|8êº;Ø«ßïñyÙ*õùþwgꢮÖf`ëÑbð·Q®NÞ{¨ýçu"6ï¼`ÞgÕñ`ÞÁ¹MgUÚ±ÓW#-»u£vòcrn¦¶¼_¥3mÔ3G=¸åºý }óû>>Òç®!6SÏÙ_á(ÎÉÑøÅÇ¥}|_êüϵa]VÓ;éö"µ<OÊ«b+×#.´¾ù5ë4;¡Ò~ª¼ßª«ãûÒÓòX·Êvt
å<Çû·p?SüxªMýzðÊ:*B5´AÕWÞÛ$0FÅâõå£Æ ѱýÍcm¿§Q)b=i|"aìà-{²â½vU¼Yý¹öUÖì%&_x%nyÄ=¼7$
ogjÒôÍ{_Åý7þæÓá¶3²½R$>oî§HS"¥"÷sKûôÕYXï»ùÎdãÉã~ðèCîÇÖVÙ×9íQ½ñ~¹Ôè(héÊÉ!欼ÎòÏ¢¥&[^õ·¬ \/¬°Í½Aõ,mqÉyI(<õA²ª0æ¬ìñ\ióÔ~ëÊÏ¢wW
éhÙ2*¬ÝÅ[K[ÍJÏöºMïôK_\ñ,rGÐs;¿±¦düÐi%òýU5p`øÍ101?p¼¯d¿GT;{1"^kqç³÷ ê}Â#D¾ÛG7½Ôö¬ ñË÷}·ÉÑõå¿82{ðÓ±7¶)Vñý÷#-ü£¿Ny/£öéÄußr>õßtÝÐd×8¢+ݽHM8ü¡ýòGnü¶~£.q©óÀ]_³ÞæO&1«Í¾!Amâé)×h´.3Ï5Ìbé©{üJNÐDó÷xÕ£Þȸ®j.îZQ[< ¦áÇbøÆ0a'Of$$;¡=K·¿µ^ìó5FA±·n¸ÊÕ}Û0ºúN±)6P¾WÛw4Ó¯¿YÔñ|P?/¤ÒÙ,{¿g:¦/¿3"~«JÔ8éòb©#.ìxòoòæ³ÝßwbP£?t:¦$£þÂ#.Þ®R?lÚæèSØêÕ'owÊðjçp¿xgÙ´òðÂ\ÉúÞsâ¡Ø5;fÐØ~ÓÁq=<#-.³ÆY#$ÜôüÙbÎͨìõ±58]gU¢ÄÍ8z|ºFy{ÁGI'³aßÞør£îÍÞ,ô#wS!>;´zÖ3iðÈî+þ/F>·I1da\°¹âtz;´õ¦÷Ûë.¿£G#ÏØ¢ºT#.=éõOi¿íó$#yÎiÝðWi&yZ_3ãé'-ë ûy¢çf=êr¹ÕÒ'ÝäÕA
ï4üÖÊ!3¬¿e:Ä^ ¥Äº:>¾ÈIJ~¨ÿrxGËw§Ã Hæâ¥ÇùôùyçoXÔnk`À¦âPB_(ºº ã¾jµ.ß¹zûaáätîîé:{h·ÓSUJ DÉht¶¸¸<çåT½³ìÎÜÙ¬WGvK¿Ï.ÛVßfqs¹çá¯z5ñsåNmÎûíTÑ¡cù_¾_FqÞ[';=KÁYE|(R*ò9ò©jAþûÀ¹#-:8I[KÓm^vmÙÏ
¥ùò¼ãªl{=u25ÓÚ´ûýÕ»³ûñ0ú~©Óe:%c'Â~ü»jÌçtU¹§tÊ»2ØÕ2!4ºª±×Óáü\¹dgí÷50#-ûmþH¤Å#⻪÷fÂ;¸C ÷1ÝôµQúúÖÂ^±¤¿÷=3#.ÅÃ{)çßVææûôü,v!GÜĬñf»Î¼zT*ëþ}¡ëô¦!~òGmjÉÇÉñѶ>¯Ä£cwÓýÈxåÍÅÕê7ámäý(8ݽìáþ>þprTØø¸EjÚ<2uÓ /#ç!Ò:BGv#¾æ#-YVw|mÛÅâaòÔ1¸âýÞÒCJ`ê3¡ÐéÛ¤lIðà÷,Zèù¨ôE¢ñÕæ7Ô #%²"ºº>×v¬®m®ºÜÕÛmv£«bzMÉ;Øj×gYÏÙ9k¼Ëí¨ÛbÄNXµÞq)àúÍļJ£(ºY¢P£sz Õ@QUÛ|Û)/²´°w4-àÅb`S9
))ÓJ«¸Àä¡:²a#-PÝsaOå§Ô-ÐrXÂvII1U.5Õ
!Õ6zÞ>uÄKìq¶"Wd[;à)àCÇ |SÁQl^W=h-oxÐ*ȵ¥Ë¨×[mÍ[çí«îµ±#%-[$)MJÉj@Îa#%iÙM\éA¸z£Täî"\ÞiY\è
qÂ`ByñÏÑTOg~ÿªgµ=ö?G]¢¥xê*ÛôZ;;Fáßsë;ìÆ88§}/ydíÉ6f`ùèËOàz¼µÕ©ãþJ;v§`ºyLÂP)wË¢ÐöC¶ç¨·÷a¿\ûþáMç &î32ÄÎîÝpþÂÎÝÁ7øÒ³Pv¨hXüÂb#%"p×®b#-ññ<b}ZÉÓÊcBþõkò°à»þ§:0;#Í ótbùòò#DDÒ¶â/±eFØâzUÐ^©%|j".åZRpliåT)`°|ãï¼"ÿcmz@É-µ1Dþøôiwg|)òãôÁRØvÿô QoVÌoÆÆgk ¿³k`Ùï¤;,EäG,iø\YU4?O¨ýtÚ4{È7`µzò1StGùǺܴífÒÄ?RÊÝ,Ïaþ6¿AGƪ?¶M|~è%:¶ñò¾¿ÍÕz£q@äâmίh²UΩuûRçHX&l}uüì#ñé!0IÎMlQÑ}uKÞ{+£÷xþ¾£I$eLY#-¿ßwà5?/WËõsîïº,PáXü¹§úË
?6¬¨+X)ʪj Jè|.SÏwÝeËSöÒ9RHóLüøå?æ¯#%ßÃgÃqPæBG ¶ßa@-»«ØõívѲt\\µ¼¢¿ £,÷Zt},úrTmÅEdïS¬!eLÌÑÎ~öÚ¾ï¿eÎ}q÷GFGUÂá Úì.ÞEÁG{¨£»éË.îªq'BIºQI7b¼è¾¢còü«÷þ=¿Nüm÷myz
±#."©@I.A$À$Á¹áu£tìl;+÷Ê[Ê0 ¤³:@-jå¾¢uâþâ0F:VâF1ºÒl2ZF9K/:àÀj(-Ù®áñxж~ÇÃMý¬i#- ìF0HD1âëúÑ#-îó³ñÚviËþ8xG#K¸ sõÂVµ#ô³Qf²!³Æ6Î-]^Õ´r©ÏbÆëÑØÝ>6á+Ol6E(#-3gc¼-b"sxø]A0ñGJªùÖÖ[0¹Ð
%ÍÔÀgY¾Ë0HÁ§_9#¢|lðØ}zAC¹ùC Ù ]¾¥»S{ùÞuÄÇ@#-zç¡*NÜú)ÜÕ5YuÏC)B*FSM^ßYfåW¡ Ù#-("D©'ê*ÝÏTª=Ýåoi´èÜ Iy|9õóqØtÑ=fæüo~y½æ7vºãÓ=þýysÁ¿aBO[>ê ÏË *ñá=üQíG: !ßJiMÅ¢²íj{É?EZáÛÑíÉYiËGCÌ![êã¥né¿CgL?Gâååå0¾&hõõùþ³väß#±xÁáÕOðýñxHBt$"Z1§®ÈØ^~MúÄ 7ìX¬~įåk&
ÞX$ÈöB)DÝ#ÍyÙHÈø(rïM1©ä1a]ÞT7¦[Jê«9]ö«°,Ñÿz<÷#-»Ïè'ÝΪîR_¿b
¤lÚø'8-]¢]3 ïÖ#.Ù·²Ù¤cìζñ<´Ê!¥¼ó#.ü5i#-´Tg´¯ØB>¹Øúüc·«yܾúìë0<W5c½Lûþ»5ëÖ¾Ü.<¤|¶DÜå[æ»!øÛb¶:ëãúÐÍÒÖýsñK¥Ì§£÷9¢ìúºa Lº/Õ,º¯ª¹¼#.óõÕL9Y¨°K÷ÆÑT6q¥YífÜL|1|L,ܸFÌ)¦,¾òy\Ì^b''èÐ>]:{ 1Deû³÷õ`£Æ7\9Në³Ü³ÎÕRb0ù8øù£kWo´KÓ¹³ÎÊZiÝUyB<vs½óy5ØÑë½Áǯxñ<ê¹ãÄ7-æQ×z|/ü.¢%÷¢:+ PhEÞÝ#.ll¯¨³$§ã¡ª@`í¨ÊEÖ¦És'K®¿L7ÄÄ×´²¶!£Ý#ç¤?º½:wÎèr#-39±K"UÀýÕòõvln@ÿRÐRlRÛÍ_âöwa÷Ô±á·,¬[Z%Qù2¤ÃvÒ¤:[ÏAÈ©õÏÃt]¹;Ê´A%GUQãÎóµÒ$ ´ãáÏï4I®îkçf¥&òM¶ÃPËgD(ôûèÜ":ý{N,¹s½VÓ`Üs¤bÉ vÔ|Ø>#.ííÝ$:õ)ÑòØyé<ºbzáÑ#-¯áOû¬4&þ N"Ä9BòéÄ52g{M{@x¤É3Zùp=Þ»1«ô}eb[^ÜX',v=h3¥é#.mökå²ú~_§+éU²¬²È`{>Q¸(¢ÜfQ"_ÉIXXi&ul¼U>»íÑXÖAÈçuåòÉÙs«ÎDZ¨u#-a²@[®9¦ÊÆö)ÓKÀv>u#-{`ïÑHj¥QÈrÇbÎé&Íݦòj¨ð¶óiª³ÛlC;m¿ëc ®ÂWéç5Æ!Âêè.ôöcÓÞÉ«D"TÔ¶;>¥Wc]rÌsl³Ñ¾LÝýµÀEÁMeJJÇwÔ×Î/ßǧcÛP>Òd;¨Ør1#-/ìÚ®øƳìì3-Îéé÷J#-ϾM±4Îu )#-Ö¶ðÃfVN;a®²úå#-)T3ýo:bá×)vJ=V´¦ÔO\êѬ綩oå]ÍiåÃ"ô#.¸Xq|2k_§¼Ïïð8£*âi·Ieõ.3¡.«ô5;u0lÉ
£)Ý{Q|ç)ضÚS¦rÒ`|ˬë5\nDmçÜxïÙåÝý]Ü9l¿iö¸ïÆílãJÃ'óC,4ö®hk¹¼ÚÝð:6@##-û´Ù¡óLÚäªMÓ ôii#%+¯FÍáÍ|±yfBÿ(°<0¯R+/ÔÆçØ5ÅL´¿¯RÖH¾\ $YBÃ>Ýçã¦~¾Ï®ÀüæwSzL/L^\èë«UM5fIÛÈWÖ4º'4Åé}þ
ËêõÌ%ï8ò:òy7ãñw+Ñb0$ó¨Q°-.t£ÔêAÐÝ]®öÐë¿?§ê_gy;;ïbà«ô§^oíc;Ì[Ã%TÇD á6%Ïbì.¼æöäeZéÉ.ùi¾¸óÄ_¾:'²:©ÛKÐ%ØB7ÝÖChñ)ýQÆÇd±¿8/we5né<yô2ÓxÐMZ3!I§ÌÍõ9À³D¬Êl]ik9û²í¬oÇgú!±Äá¹s°-`øUnó¬@ûl&>
£7àçã5¯m¹¦¾O.°ÐuÃm}sJf'WWMÎJ8 þ:o-Ãh_ý·}÷ñÙúb]r즨´ßDÇx#£Q|º·{§áÇsv`Èd=Ú7µÍÖªLkDü3Dµì(ÊXÃ} EqS÷
4HÈ·;ÃBôuqû7j #»µ_ÐzZOXñ7+½¸³ZÉy3q¹$ûKLQe[ñ4.m8ãVEØMÈd÷ ±2!¢m
Ó-í²ußJ0xg%k¹ß=nL¤¡ÃK"¦ù¯Ó4ô#%²U#.çXB§ÆQzÛufÅÚpâxk¾uèÔe^y(_#.7Éh×±ÙËì×zé ÞÞÆ
ƨ"ÉíiÓj\)¡µU.LYÊWä«E%ßmAllm;îÝ8óá:óK#*6k¤æÓæ&Öæîíùåm÷X¤¨W=i"^äPX±Åٮʫ:qs¸ö0'ü³±T@øhÏO?^YÅ
*¡ó[ÎÅó{o×quTTÆ{÷Otü¾ ³mµ Ê:'®CÎÔ¼M4Í¢fh-ÿÓñÏWãðøÿßù|Èçc>È¿ä#WÒ U»0¤ÿèóѦù°±Ií±#.×uÃÎÍÝÑTGåTýÀ¨ª£ºTiäqÉÈÛã¢k{w}ýCJ}¾\ÕÜâºÂèÚµA%ú¾6\Þ1E·_PSÑQÛÌæòßVJ4ãY<ýÁú|níáú¿E^e.:¿U±©=TåÙ#-6¼"Ö7ì1?b¾ûÖ Ù«ºè=Ë»ô~oj±oÓë7D~Mþë3ÆF4»À°0bnoÞ%ÕòÒËû°ö£·ôÍÁ?Äõòalæ<ÊÆp$¬ÿãÈ 0?ȪªýÏíeIJªú&Ò#%S ¨pí±(²7#-C)`¿¹þ¢ÐeÇA§û_à<Úê÷wq.|¨»~íKú·Òj°ÈÿÖ(? dä=â`sríëÝÓÞo5LÐÚv7*xñ:ÆaߺãcJçZ4ÚwlybP¹ª¯/LcT%ùÔ£m+l³Zâ(ó?×Ìl(¥±îSiØ£ÞaÒ|=Þ'è_Yõº*°õmOô@BÊááîv© Yi\½ÏßônÃ1\ôcõ>rTY÷ ÙC«§ñítL§>Åèÿ@}¢~Ôðþî멽<zsi|,x³®ÅVV·ÐÒ#%&`«°3|4Ì¥?BéFr Õ²ouSsXü{|̦:¸hF·RR@è70é¨E(ë?oùKßòLæúT?>Ìi{¼z¼¼rÝ_²÷íP÷væ.|äx ¹B*Qb(TûÌÁÜ?A´Öd vgCtTOïÕX¤é~bâ@÷
¹~o¯Æºÿatwôq¥mh!@X¤R£¾dG>@Gð,5#+4©¿¹^)ñÔ3Ì)X¢êIäÈE®®¼l>U%Ä ?$BBý^zj«.TÞÒ`KIâH ÔlhM¬/Âý#- ÜIÕ±6w jø¼îuÅÌØ¢/UòbÚjW,¼è!Á1ä;ÞÂà»ÒÉÔzf¤bôEÌó6fHéR¶v
æL#-ª¸3ØÎè~~Ø$}>«´³ê-oäõø[Íà²9QwPÀxM¿>t#.:R!íÍ!ý+®÷°|º¿@ùK
I/ÉyBh_úÿÝçxÿ§PÞ³LÚ^ð
&°A8
é~×ëëÈÞEÁæù© (Èó&¼)ì`©,ÿÁ¯Û¦Ö¹Q±ý;äÑëEËËáæàÅI!¾O?-Ìi8»ÌÈAßÑÜjwï x"!»Ð7~Upô®ãè9ÐyÖYwd'âGy5E¦~ÃQó¿10b süÛe½êk½ó=lßÚÈwh#.!4~¯iüUAITHÉ v°Ê¼«j
EÚÑ#÷¯áÍÌÚs@ 3üw*©#-áø<Ëò#.yD!UÐAªXÏÖmnóëw:Â:é0ÿX5s§nãyðýJ1T
Tõ5À§¹sJ~vôtI³ÖYÒTaÝ»¤BÎb"òįÙéìÍl7<UCoåëÞÃ×ùa¯§Ï8nzµÞíL-uËzXu³äsÃn[laÙå¬@L257º}0èo#¼T½GÌ|L7ñ?ܵá#%Gò~þéy}Õ%CÛ#Ù?Tm(%"sí4§ûçæ1HÖÀr¦¾BF÷Û|ÞüiÖ7vkbñ²5wwLBDáøLìEU3È 4@Pº,L#%ëþÜ|ßî/aáar׿R=(êl^Õ%¢&Ì?;ÇzïgÚk#%Æóϱk.yõ#«Ë2ÝfÍ¡§KôLÂJ#-ð¥X^µûôqÛ¸?¼ÚWÇÌÒd¹Ñ4.:°ù#.QÛöÿ¯¡#-¿`}|J¤¦A::ÔÜoOÓ§pÀÈäYÜoÄYàtÞçAØPÂ#.jòàd"èSâ\7,Ú:J('¨Ã5oåRü̲SvHEfAiDÑfv(7È÷kSSxIGc#Üs{»Ê÷NdÙ>aëó,bHÔ$4Ïúþ[jªB£°ÛÔMbCóø=n¢Ð=¦ÎL½áp"àåh[M&vD Ô÷7Èk/4lcóÄ×Ø=þùb;_¿FE;¿R$T[ê{Ú¢àhUnÇè-ã}95i6+uÍNúïÆ£ÖfÆõ-dm6g{i¨,Þ¥tenÇÅ*[=2i¾!$×U¾&U3²`ÏÕnæÓ.í34Md9Z,)J`B» -HR|J6ª.G»J³0ÉËI°Ö¬Åýf.pÛPqëñþ`^Æ,¨ ù ¿_ÞfàgN±§ö²ËZÁ~¾8~MÙ]?ž¾ÇêÇÐÌÌѨÒÂ~ÉÄ7xàÍ~AUU[¢öç0
ï%4vw{SëOoØxñH´>ÒÐÉbÁ£´*[ÃaGvV¯®ß}Wí}T¸äÇõ«gSkicß=çZ¶]ð«ä/ù®2mà§á*}÷»ÁC»#%Þ¯*ï'ÒyçI:»hPb©@Ä7ch¯¼µ¥e¥U1eX(¬K[knª9ÄÌ!34Ù=¦RÙɹP1C1È? 8Î9q¡¸L Æ68Fü?JûP3aߦþ-sèZI Ä*~WáÎÇ?ã8}kxÈó²k½Ô*ñÔì1Ç^·#%axAä=NF"úÃßrÙd#|¶ê?àÔßô §ÜD¯ søç̹ó¤QëbÈnÜ'èu*^Á³Ìá#%»VÀài@¼,l7ÀßBW0âP;´ç|¹ª'#%üþ è:$SÀµ#/JDO2ÀþX#øCllÂCÒ`aådtÜ'Ïto´h©þ#%~¢|ûxXÏæúl#.HÀ2ñfÃÐoûH æhç¼æ1ü·Ã a#-¿N}¿ñO ú«,ìAØÚÇìT>DZ"(6z#.ëâX2~ä=}¥ú|¤ùnÞYÐûF@çójè ¤RáÒèxì{ÇÙÉ<òÑÆÃF¯©½ù
[»P!¬ýð!ó0uY¹y{Úr1íPì"#-&ÞÃÀA&&kFÑûÃÌ #%9a§Ç^q1kÁ¤3@æ9ÊlÈz#.`ëraÒÅÔä"¸/\³lFá]·ùQV² ~½ù+|þ7Oì:ÖàðPiJ&ß+Hx'KæüZÞ<ÀÌ-C8öNÂ#®o({þG4GlТ:u$ö#%íâ>@
lÈú=è>©3×Ýk«xÀïÇÆïvο¦òñ<¾ÒVjhfw6Ôj¥f/©MÈ·bùÙ${¸#.yÀÐôrþ+jkä©£dh cïú½àKË{31ÄD´6ÊÂΣð/?ÜÛÀ2¾Ym3åÙog£ð¢³#%ØË3t<ù¡âßõú÷£©#.ç_aò¥¬OÈ53àúìû_Á°ïF: ôØZ"èBt
ö¯8êã8
'Ìl <ãg°Ü|·ó÷Å¿8Î2MÁënæÅÁFw¢HT'jìÔrG2Rº@1¿#.&$hi{µTN;:Ô)1ÅC~oÍú-í/m{oÈJfÊÁ©rï|fë]bÞ;±°fô(ÐoǨϤ7>×Aè?1áÑ3ÀaÃP!!^®/AØÂ#%Â!õr9óÕmRíUeYEÖÜg±'ÁÂ}»ëÈôøàfË'¼=Jànþ TñUí6fÆr<hñ#ET¢Ç·äò`³#ógíêjhñ¹#%«¦·º±½I=[Ùðý1#ÿ>ÚdSý#.´9piÝ8Ù©LÑãxz8 ôfÓæsª7bCô&ON«µ-k÷îl1H;yé5·séAyÒÕ{ûªªÙ¬qsúÛìw¯SÆ;}²O`9}±#-ÛM¿¸Ú)ÐöF@$$JYQZ"|Dà}@\úð6Ý:κS¤úBy´nr4!c HxÄÕw;ý±oe{I¿ÀÞ49õhäì.ÜîQùøPARìI!"¶ÐÇ qq"B#%\JHZ5]oÀbcáö6ýðèÜ{|¬_z±¢'¹°pR`±»È»T4Qb1Iâêgî6½Q*èv¼CS¼?tad©ù'®Áv*ºd3bÙbjXPÕèA| ÈEH*83#- äåÉ `Õ©kôØ#-¡Oå¹B6s<<qGõù¯f qXE"7ªÒAv°²WÜ¥ÍSÐJvð5S2>¥¤ìVÉ#%æþ¢¿ãûúS÷#0û
Þj)F(ªAÏØ|¬¤×[Ò¡PAb""Ë&RLeñï*øúí§v®jó·ªäÙ#lÛ]×;l(:IIb'èÁ´?§§ÕLx6ο;0&h]ǧ@÷>³¹B â»îpýÚA Céñ&ðà hf:Ï»E2w]Î#-(ì7- U2È0-£À%2È)tjhÈ%ô£çRÂæ§>kÐHßÌ6ð Iå»3è7BëE¥Ì9@Ü6lÙ»:÷¿}PqÎÐÈ:z¥²!´à:J`[]
lB·,:9Ødy´õÝ~vÙÌã#%ÜhF´ ÃÊÚû½øDë!ÝÝc:@¨nBÞâÔÑÿ
áì¥eK WRª"#.ÁQþà=üÄÀ'µèM7T´õrê\¾Íí/?WÆ4¡ØyãLh!¤î'IG-l¢öç¸ÍIôû ÐptéÆ`¼}©k#-Ðj3@¢ã$
#.'ظPü,6꣧$ÔYCú¿9Æ@9#%íÚ¤/b©1çØð:@ÕOOgÌÍòäj"IÄðãFõzNÓÀnW[Bq#.ü!ÞBÁGÐüó3¹__íÝMÆñl+>ÜB+@߯QFtn-EÏe®=ÙMê?,O´ûíûgâæ?*ÝéÇãeèÈ~/·ëËYÓû¾ÝØGÂ5®}öâÌ·ï1!!Eç7&.hGTÉþn½Ðaé(¯®/&xõàµQE¹HDX114>ÆBdIY¹ºU÷¿p".æS\YÙñ#.<ññ;M3G3°Y:ÀÄqA÷Àànxj¤S4òUЬÛ>z7÷àW¸Þ§p}®#%F¾¯ªÁu ¢`A3èGˬâaNõmÐzUèÜ÷Äá ³¢B©·ÏeKI¨{<${ìRõ\ß`ÝtãW bsâõ"¸°D¹$lØ=¯ÿ^7{¾ÿ,tO£¢ªÒƹîáÉ9r EôúûÏäñÁM;µ³øèÇ8fÖȲî§ñpu¡õ4/Ç'xíTâõús± ´:;"@FZSýß*¨@Äí£Ee\âӹ?,Lºzfg2v_à4ÎV´úaݵ
J]ß®ÁαsºPòæÒ3MÓLëdÒC'içð97_h´§wzìt¬ö»IÉ~/'.@FhI6Ê£pi·%6¢ QÄû#-ÇÕXÃ%Bq9Odù%úñÔ üêo])UÏn0 Ô4Çxs7~]·LÑ×`Eæ¦j;#%Ìî^gãJÃ9!zaK@±¡#.;x¢ºÁ/!{`¡\aóa0âAªÁj`«[n»Ëòm!µ;f1ÐÅdëMñD=¨QÛÖm/ßÅ8jw'é>d ²|HÑ~¦?k)"hA%Æ-¤ºJÔ0Áo¤[z9pÝ#÷Bäyßg÷YÎ#.[O\ôèû``øÑq#%z¿}ôJ5iÔKÑ«zndt'ô·ÙØçíâ¬7·[C^Q÷aùÙ÷N\>ýé*^÷«Æ+ÚÔºþ¶u
Ç*ègègeìÀÝPhf¶áWm_?_6úòv7§ñ@;ë\ä£.ÅY4WuhWÖéÚvHý áà#-çR%¦¤x¸BÀ@ézÅå}Â!k#´è&ê]~¨¬o#$"@)Þ ]PÀÚ<WÌçØö:aÀh¨ø½'FXh*l?á«aôñ'êrr¾#-ðüùX!½Ák@ä(,Ï¡äO¸N§qoËEl{ÃÒ#%ÞÅ N#-ÑÅ´#-#%õ¼ ñ!æ3ÂàR?å÷ßµÞ0Ø´V#&A" ¬*äüf>g{lÓ_[É¡CxÒó¾vñ0Ük¶4ÇbÌ`vÖq¬)D<^e }þ¿£Ã~¶!öÚ5$§5ìÏe® dó¡I,Ûfì$Xª#.&t§¾¢ecvXª0?#%ÁüÝÜòûý¿ ë¦ybËê½)êÓßú'Ì4_h,ĺ(BBX ;=èvBöKÈäÑØ#%YâÊÆný½Ñflò@[âÃo·Îõ¦±I!]>ðla=¡#%²¤
gúÒ{¿¼ë¹Ô^e]ÇåBN©Dgl]°V1czutrMü{³µ_Ë|}º·÷/"ÖH¥#sòíJôÅ57Çé(-07¢#{ýífÏÀp{%mÖÚCaT¸8ª/bDí¤À±_K3¨ý÷ãê³Ñ»»¾À,ÇOÍ'!¢ÝÄÆìõ½°a¨d#%ê±±U×VkµMÈÜ{#-ÞSÿ61KìpèI¯ÛûZd»f?rfô
2ÙóÏLùÚ#%:yjêû(£ü°Öö¢ÀÜOµã×^HJ?³&é) tPìßß=Çíµ½¬Ûý½øA¨©að~ÞÏpT`>~t¨mdPQ¨Â1ÈÂ,Q/êxæÑ¥ÂâR%ãõ©±°EÒÈwYo|è³öÛgøúÀ¦ÆÕxD±Ã·:±¡#:y÷þ+~,û®ÙCá]ÙÚɧPb%/G´öÕsæhD Ë*mÂ#½+'cø¯rXý:ú^gÙöcÕ{àÞÔ¹EýSÆ\ßÁ
GÓHà¾×¯¦yÔvþKk¥ÖZæ¦o.qÓ2Ù<¦yÕ¼´"¯/.ó5éÀÊE!Á¥iF!l#%ð51IÐV¨¨ÁhÒÀ Æ2
¥ÆÀÂI. b!(`V#.0,TÓþ#-©üalfM§úÆg
±c1(Õ&ÁråÒEíèÐ\f,YpPÞ²Õ`¶rõÑ&0$ËÝ*7¶À»z6`mwv0¨Go}ÑRP»
4s.M̨ý¶ äâ»Ô¯OdEq¯Û×aÎ8~vßÞF:³v>1<¡3Eò»éoÄÁ¿ð¿÷.:oHb©ÄªnI*F2ªt&SwûëJ=¦tÈù9®TJßí2^¡¹RTûÿ)Bá ´S-Ò\ùXKÍ*áiïHIé@8÷qj³ÌǺúg#%Yç½*¬°)»P×"´kØ\427ÌxñQøyÊʬï´öGq6"Åæ_YDf@vþ8¡2iì ÒöwÍßx®z\fiÜ¡#-R[#%Y#%¦w}ÝÓ8D I×ùî^âÂZ«ËæÂý¾j!#Ê#Ç|j&ó=c@áµ-¼ñ6Z¶Rn «®úÂ1Á¤Éô¢`ºäuR®Ïò;0oi8cQÔ=ÆsVðßpbD _*âIåÒòû9út=xxlõqsÄrÖÖa=íLfîйú8g3O¿cÍ2n©qC(çà)A´K)#%MÉNK /\ ¼E0rùëíËÞ#-BcÍ×°ïæÓÁmkºÄÍSÍ'íP_Ú2iñÅù+V2®ïÃ;ã+t|7vÒ*v\zæ1~''à1ø_XaÁÊËwBô_àpØ»»dÈvJC$@=}VËãæ³Ê©hkL²SOá:£þßç)ÒÐÕê3EÍÑ'f÷bï«Õ#->MoÎ9.2cBHëËãÍrÝÔN¥c%¤N[§Îç DÍ$¦DSañƱ®æ®ßK_#.vðêf-AÁ0íd9âî;Áûa¢ð¼Úªî#-B'Ý¡¦Äg6¢
{F'G,\ï Ýμàg63227ê£Uû æß2÷æn8|%þõÚoÒÛ£,¨Xq4¢®f5¼=#-vü×~¸Ôuî¾ãæ<¥UÏÜþfÚf¾@4}Vmá£è«é½KAFD#.2)¾?4ªó^_µtßÈ+ù&#.×EeòÙd pêc¶©|ÁW#¶ú§ÔýûmëíýÞ¾zêY+#-CÖ[
ß«åûN}'Â3þqãßå´lmgõܵ.|õ#-ê{-ÐxóòGGëÆ´7Öü㦯˿Ûf.2<-sÓMòÞaZvZÆQj¡\Å&42CãÚòyìyj§F¿¶,Û¯1ÚlîºEÚQ¯kÙË{Âc¶¥º#.äs¾¸Õÿ
Iûq²¿gÒ°®·:Èt¹æ[ÑÑi?W÷GiÚ\_ñ>-Øñ¶ºd¼ ÛÝTBÙ3©æ ÝÍ*¹FñéÔÆÏ;ä3¬jx[ùì¸ÐèÒ]2ò¬Énèî°PÄ1J£A©ISRTdåÒæÑ8òÔäè½õºÔ©×·RÂb!J¸µ_ÅTàÐý.3ÃMqhéÖ8bªÇXòYÓgú¸T5õ~ÓÏóbþ
_=»Ú3 CG;£ÇkÂ'¬6CQCWµY#²4Ë#0ü+説²Ù=Ô×qæ¦#.´Q®s{xvØüIÏfÙº#îݦÞÿ,VæñµOQ-8r/eþN:Ãë&³õðØÌ`S¢ÇÝÒi¿ÚwàöÁÏ#-²,ã¤Bïq#+¼#-§#n#.5Ö)37?£´y}Qãøh:U7ÅtìÒä¤äÊXédÆÔw¡Û\Òúõö÷×Lîi©æ§#-=L#-7HQçò¢û
/Û¸ïûV¸~Vû¾öèíÞ´}¾Dm'ïÙn¾uTDWrázK½[?\ÒQ^Ð\ó8?DbnÀÉg;Èèü`é)KÙ fµnÀ}cG©Lôª£ÒïTf;n&¤!W+®PXñ¦¤W{ÕFTÛl@w~2B)¼¾x³¼Ù í çß0:WyäùH,Éj!Ð!VÂ8D÷:°é¥'d±CyXe8q=¶D8AEÌ0pæP*haKôX+-©p¤MO¾¢(èM )¾'àªqÓZÜ"#uÀÚmÁ±Y0ÆÒrÚ[öÒ÷/fsÁbÅøøúíÖ¯ÇòÏ/D0»#%·1RÖÌÁÆð嬯«7åóãOàÑ{&CGn¯¯xþÚVµºÍ6îëUŨÆ\Û>=¬LqÝ0¹XM±:£ Î4O©çxììÈTRëüw½ÿúçBXå:Qq=_?;ôô79ð
4ÞÕ*f~ëÖÄ(_áiþ)(ÅhLþQ4V ^w?Ãöy~ú:CD?Oï¡_¬ÄÄkñ{Ôå&ÆÍOàÛû;°oí|Óþå|ÃtYÅ3ó§òÁËv#.óÕU[G÷Çø¿x@±´wP¾¶±SuâØL)3?@Z6E $VmÚ?¤£,9dq8!F@ýî'ë°ØÚÕXLãµð5 OIQâjȹ¯#-Z±É$ÜIܧRvÔP$Aƽ³gÄsïwMm7oÜZâ¿"«½£üÇ7 ·>Y¼aµìuõâÑ!G®"D#.Üä4#-1ûÎGy¿úFÀhaUUUyÕ7$qªxâl9ÔsÊä)¾ß.ãBn#.NGñ?e?§í?.m)@¸ÉHÝL=çtâ<âûONt-ðlt Â6$+#-/k»ê·ñ +µüuuy³I¿ÄO]Jå(¨ËXºpºvÉûмÝ#I1¨5ª00ÐÖ0ÐjÒ¦CAöIÊ)u°©Ölů´wr_"æÑ|8;È#Ó¼u][C²ÌÂJ0 d+aM#%®ýZ£ûQÚÜx·¥XþJÿWé>í1IömïzéYgñëºüe9|Õ¦IÃë§í²ÓÃ5gÍÞÃòC¯¦ÐY½ ;nÑÙªmI«#.ÌÜd¼©S7rÐ#kÖ¾=¢·0Ð~BaÝËq&Û!]_»;ÖÞ¦k)`Ú?±Þ×7Y]MÜG]^ÅQp*oófÓï$øHtbª*µwmÙ÷ò8ká? #bçNìûY·*8M¿ÓÀÒßå°åËÕ¢¯Í#üó·bÎÕ ÒF93GÏôÉFÇ'3SPÖÀ· WÚ·³Ñz¤i%éÅ!EâPÐúû#.bóØDÚu.åt l!¼Ç#.*¹bÆÂ&ØxûcUdøa6ÉÑàLÈÇ%¸d÷EeÉDÒQí}#-FúÉ&]ý}]ÞÏ_ìÆ3ãwÀä°-Æ96·vÛ°\ûÎm"·»n×Ë HAæs;"¥¿"Ùé»Ì#%ôÝG¿®7x$¹}gF}%»³öu¨\àÉ p_×õ·Rê/ÇØirúµ%ÀÜ#-¸½I´ÍîaÔpÔ.nÏ¿9*¨¯5=·§ÇykÝ¢ìdQÇÑÏ<׳&7vÞ9Ìȳ¼OZâê9HV[5mÜH
M¦oòpêhÙ^¦N7Ø\³tm|Ömäó#-g-TØdò$Ci,ÿQÐYT½fW#-Ó<®#-) <˹ðÑDþb¾ßvÃ>ªß#ØuH³3Òq@ª¿VQ·}6×pî3°$ÐCdÚª¨_¼{|9:né·ÓsÎ&h4µ#-¹+'êlßÖÔúê$u¸Î3Ó©²ÄMóâC@Ö2K/_(ìØ!ããìýúáó]ÎË}ÁÅÉ!P¾zÞØÞ
÷y±¨pa]æä¹¾Ê96G¸ qC}» ìúËöêv^±bGÀôB
7æë¹s2,XNìª {<I¬MIÔqSÈ9yn} ÝÉ¥FÀñÏ¿L ²ýº{::/ÅYÐ(Ó¬aD8¶n|"鵧+î2å'L(]ÁAR¹£ßÁÒÛUù±zvÁÒwÂ#-ÛÉ°£¹à&ÇuÒ¥èP,;!|å#-!Õ!fmP=>ÓÞºö1
©wkÂp0röqñA&å2.ìe»V=¦¥Ïw²y#%RF"ÁBC¦4ªvݶÎsfU6å2Òëtäb Æï_/41ûÑH#.H°¬#,=Hëq·´i¨»J
0£rËlÌè]8ïôÑ8`u¡CТxq8+¢p²Ut¹+ 1$åôxÒé`°ªÔ,õëß.¡ÌÈzýÝõLe¹V9qæfL¹71bÈ %LÌrÍƹBM^*Á&tÑKÄ"°XAbD<*KÁáWBÒe^g;½CÂã"cÌ°FO£ï¨xú{:ð×sç¾EÓ®ÄHÆ#.ÝDÁoä0ã.þíê¹ém"ÝtãmâµlÝ¡Ï4ΰL.³:ÐØjeb¾*AaALär¶ÃçañÍg³Q¹¿` 8êrÑcªP;zMrwd©äUC·tªAcKT ¢ÐÕ4"1°\Ä·Ço©;I\[H<f)¡ÇFäÔØÌ@°9´>6Õç¯k)W³(]¬iØl¤-Ç
;MSG .#-Î'#-T!oÞª¬ë9j¼Ô¯19m!5D{°zwÜÏ·ü×î6±@Ô©;Ý£q:J #-ÛÂF껵.¦çßéµÐ'ZÛì)Ðd¨*#$ìËXRfMÅE;û¾ñì£MèâBM°ûI°Klp4¨Äës=MaݦðÖ¤ ~ðªyx¼ZWîÄÀ0Q£8¬XF©ðääb{æ¬pø#.!î7) ¡Â5xI61ýLoÀJgv`Ú½^ÌHB×Ee&ß×ø`;÷
Kª1hegpàÅb!Ö*gh`Ñ>5àLN®7å.XÉ,78óÙC#E¤#.E¾#.+q{æ´Q,;HöDfCtÙ$¡§!p ®þw¬µõÝ2=ã{1¹6ùlªMiíâÃSeùíÆ*ÕqCÁûÖ#~
î![a7#%l¼Ak ÒâRSÔTg& [hÄ kµÅ!dÈ¢VE:¥¡Whëq²Â[Ë%5X8Ê#-âL¸¼ø¦Ç`x+#-¸S72)°0EÎÆÈ÷Õ¶{v¡¬y=Ø%<ÕCin" 0 YäRc pg$[جÉyúÏiÑ\ÊkÒUz#9Yo#N¾ãtÑÜÎÆÌW\l#-¡F×q[*/jtì*J&/ÅÈoÛá :_±UJ|³8ãåId²6Û<eÚªªª×æ÷¹p,¤{§C>ïJnxDF¨Ûr
=¼[iÄêÒ0ãÚ¶Øï¿Ú¢ßϽ²ÀXÛÜb½ÁØöÒº$a¡´Ú²Ädɳ3#.ɶ*É _÷ú²Ýj¶C5GÁÞÍö1£+HiÇ0cn>Ó?©ÚTÏ)£;e6P¸Lí÷f,\&úÇãµæO§±ëgÿJ?ä Ñàé#-Åq]~ïFmë¿É$e¥V«ïÿ¬K&çvÚm1ÁÁÞ6ʹEºqNrL¸ÀÈaÑ©Äb)VC¶éÚJT+wóNDòrpQúmà@ª#%`z»U£d-.FAýþ§ûÙ²ÿÿWýVÅY®U¼óü¦¢#Øy¼ê+<ÌÁ¦0!"ý{;<»Ç¹;©I7}ÞvÀõÜzÎJtéç<á1##²Úb©A·Øyy#-5¼äÊG{g-®K*³u¯áY1I$`'´E[JUQV=qu!o²Ë;û¹àîaÌÒ.3½QãeOÕöû¤b])CQ)QNý´!Ìx=KßG; t/cáb¯õëD °àTêpùÅû98hA´&B#%ü4<äÈ¿4,}ÍòF*YA4zÐaðõÊPÚ1VDݺª¿å¨ `)pýNZT£Y$2ðé1yç^l¦--5*QDd/®.ú$Bá3ýía,ëâ`)æMÎðë Ö«,è¢ùqíÅTäôT¡§ËÜTÀ`Ì#.úN
ûµ}QÞuQh`E·°±¨:Æzk`#-gDkcËt(Ý¿`uʪy:t"½ÑGÃ[ïûëµ¥*¬V*Ê>¸à q@ã Â(0¢Â*1D°(ñ2øÂîÐ_ó¾v#-ÔÄK\û
Dm¥©DZQý@0¬µÃÅ%±:Ô#-a¬)¤¹E!7 jIgÒCü¡þmÞo%Û»oH¼oÌI¢Þºï¹z©snk¤yÝ\×oæ^y¯;®ºãJÍÝbå>S¯;ëÞÚKFøDÉçßµn·½ÓW˿¾fHØP*LLX²Ìõ7.,Q=S\LÿRظ÷ÈÿZ*ÄcÐ>|OöZþ^éýyßjäÑE!#-´àQ¡¦VÔò³pîì
H&æ"µÊBµÎ5P`+¸íÍDÑNYè9õYa¤¸ùÄÇ`wNDKWÞUE,wÜ©ØÅRA.Ôc1R)3ù³9PýZðÝJ¡ä T{uáó¯Õ³!/ÉoÀÑWVÒ¤yJ!²äжH© ËÉK#.J[Ã=Ô½¹#.J#%QïdÃA1Êè#- ÂÓ1ÃÊ{X{ÈDM&}Jßõ\ØÉHÞ8A56³T+x3eºÓÖ}^XmÞlaÉ@öPZ¬ÏöÂ\ð¿a9ð§èi~+±Ð@S¿Ýéì7·uE¿ö·ô`ß¿¼ºï³nCÖäyT^6VyPz¶(OÎQB
(¤êìã¼8÷,£DØzM)|Bê"Â(ÐöÉø34dP/E*>u^ÁVveT¢ÎyáyuÐÉH\¹)Ö°÷¿9üZ\»LÍÉs[,q#-þ;òß[¿O·°ýÖlµ ÑEð7:Ò QOº {I(H#.ÆÛF)U([CeµfBd³[Ém¡$B°E >sIÆ|'+|¡Rs¨> "!D£Ú¡B$áÚ'Ý¥bq/®#.^º4-c#-"4çáz<2:ÞÖ.q(^¡©åÞ u,¤vÀPÕ91e:ägswÃäzlQLÒÄ4ÇR#-S&#-%@z#-l´¯Xg2;ë0__Õ½²(ðEwn W×w*£³ á¥B'°i7Y Yúaé¡=^)à@¾J#.BªØѸ"s~Þ#%âGÕ¹9(¢HÈHU_{9¬lÚ幤®jí)hÛ_j×4m«ÅtlÉú)WÊH¹ÙÈÁñ90$@3E|T:ìé|ýcknôþÌ[4ѾZCþS½êêÔ°§ªÎ¼ç_Æäf>TÈðBÄênÏöß«äô}¿o?øÿk,CáD>o~iI#%ûDðÃê/É· )2ÂX¬-Ë ¤¤$7¢#."¾UCèÙj¶Ó°çÏÝCWFJÅlU¢?TVï5öUÕSX|ä¥è:'Õ¥¦1T& FÞÊSê¿E';½^ÓûÔÄòR#¹p#-ÐR âå°ø{ Hî7ïÈSÄïN¦ÝļÂÃiBRªq½¾Ö·Ag©´Sf@)zÃx b³a´"±»«lº»aÍ:ýýOÊâ!»Kº:CKtA`ÒuæZ|²§þMha#.ñtzý'³}Ð9!ý4w/ú^±47Kªs¾-¼dØâu±EkUÖõ7DµMÜümÐB ýð×Ãä~£Ñ¨yåD¦h?*;Ï0Ì#-÷øùå_E
ý/üQÙÕÒEsCRD6÷ÞèK°h6Â}<YA!¦¨k#.ÈÜüG(6æ-¼8óÉíÉ#%Û9xÁH@ªDØ#)AÑ?~ë§<æ5XYV!dݱ2ÔS«ZKbg°ÁWMq
º+ßÂ$%À_&"¬ $¾Ò¢¡è|ÂÁT;z¦`|ÔØ·«Ef«òîþç(à·x ßõþzÜ,#]B+iSc_«ðéÔóǨ¤FE=ATP$¨ #.0 H½^Ì8@S0C(°6f<û;ÌvߣDÁAtØ#:ä¨6
bû±aõ³n6x<#°3¡Àú¢VÒ² C%b#%â ¥«ÕÉß±þ0tÕ·¦6å onǪ=ØZ»'ê%DJ{ìϤÚ6ÀãúÁZ7!þþþ:pÞpL¤ø<Î }ÑÁ±*D#YldAVÌ- e³ËÞz*ôîTo.÷ø\q2qü¡B`Ùº_ê"zQ©î\tLAâµÑáøß´u\x×1Ó;´äô¾ÜE§å»¸¶ÈQ0&í½øÅ·ÛpNÊãsÉR÷ÙyK̽^¡GÔ±gML$VÈî·ÝZZ¦ÉÏ3DÔAÈßåMÍõàí]º¶u£VÕ³?¹Î´ÛÒéêÞóðÞa-Þ´Dq¬«é
1£v;T¶-# ÔÌ̬!!ª}ñ+i°ÜÓÏtW#.àb<d÷ûñÝÝý!å¦#`g!«4¬ÙM¦5>Í/Fº©1}xDpYaZØ6£rB:¯¾UV`次]iªUYw®Ò/£&ÍÝÒË2'ß½RLL,Þã5c^#% J0#.Dfwlp9PÔHH½à¬p>BHì¼l6mYáob~¦·¾êÛwÚ"Ã#%¡ÐÈÜQE(/LÛüøçºà¤½/1fï?#%À;N·¼hWCÑz«öE`&X2Û×£tlí}í!DZ' HÞæW³POiË6ð¯O1ñ!º¤úáÂOL7`ð6¸ Üj~C±ÜDÊáN|:#-í¸¥@èh0jîSáªl12»yÉËlbæWÒ¼MR©J#.Âû¾ÿ®¿b°g·mò2"âBýÊ÷Àúñå9VŶÉM<ãx31#FîݧXLßVÌM\lÄÉ;}Ûvu÷úÅM| y{@Ô
TR´ê´Ð{ªª¤nÿ{#Á 8¢öoÛ&îku2¤ÓØ·#.§åËfªÌÂâÊí#-§&@þ/OÇzD5ñ嫳y`Án&´ jëØõä^0Ç¾Æ Í¿°i7(ù¨0ÛÃ|»jñ¬Õo*æ4FJM]+²w[©ewV®ÔÖ»¾ÖÝw]«ÅÍÒ5-x(µ,pnãÚ\À¸JϦN!²»ó}¯]@á"°U°7Cw(DDQ1Ïߧ.¡êêÍèN/(6ª7é®þ\^èoíxqñ[mw^JbÌÉùÒÊeexQLAªïµYJX¥)Ld¡ï£!EQïÿÀÁßGϲdê®#-é?ËEd$4â<V¦DÈVYÚ«iey$Ñõ¥[ºXsZ)à¢saXm¢+ä©OOÂQ¶ºuðñJ}À#.(AÌÈ`#%~Vó³$wùQÍì#.µg*4S§#.Æ6ÉyH̼ݹ8¦!#%éSvêëE·6íËu»Wkw[ÈÁ1Äè$P#%bIQ.|ÍÚJçB×Àcï <&#%¶Å3 BDjÑDðÄ>ËgÀh Õr0´ütqØù]8#-äï©}ß׫nϺÙèÚrÒËm±m¦¨Äc*Ï+8ßKôdkÀÝyd¡KÖ{ÜLö#.æz ±^~EßtßG¾¢¢2víD*D í'#-3kzíÃ4ª¡Hg%'3a©¨c£n¨
â?e}º$ÙWÉð¹ôûüûÍ{{ pÒC:Õý#A\(*çÑ#-Ïa»Lв0vBdK)¤CËT£»5ÄY$cïj\ Ïd»üqzØqJIñáSd¤¿·3+#%ÜSF~ÒФYbrE¡+¬K±d:t}Å»xç«M¾ |¹»/àtílúa£¡,àâ&Ü}ø-ÜùÿUrØÌCòÙpLöÉ$<Áå³Rû[Öd@á¯6ÚJW#-K¸l¢õp%MP¢)pº¸K¡òÎÈ"N@ûBÊ`º89åæA
@ÕìÏD$$$àT#Ê@@È^\ïvR¿s>4diÈÅöRae¥ó&zÕJQ¨4Þ¢³h$Ñ(!R£G#B Äx fP+°ÆGd eó1f&x°îÖmFÈqFJÈPQ¿ºNÕ(Ûq°âà½lÛ~öØ©*ÅVñK#.M¡·Ç\ÊêÚûíÔj¾·Õû¿[³¶öèzÚê踽q-õÐ9ó$§yzÎQ#T7¦|¹ÐØ¢kÊÈ
EÛÝë>&ìk19)>àÁ¡ôEúÏYh6*0¦/CWjI{v1ÚF
A1f Ðù¦´YäDJÄ=áÜ}³,2YãKûü<=}ªz
Âør{#-#ÊÁÛÈyàQÔ[Û@¥TmѲ qüzs']¦ô"s-E°E*4»=çø $ !ª 'Jïóý»÷ÛQàoÓè-zÄ©g×ÍøVQT-#òõp'Þ¹RUÁ ¥¢ÑvÕ(bëX%ÄÃpã'³aÏÃÇóÇéEª³Æ#%¡<PغCõ]X¡¤V,paù"ãA#.ÚÌ#-
"d>#(Ìp:|,raÁ lw`o #%Ò-$ 6¤t8ò:íæW¤aT°29'ÕÁ4zG±QÉ?79h ÃÙ½Â'HÔ>öCûÖû¡¦%Á4éÌqd1Yehx8MP{Ä)9BO1q 2'^´[#-ÄD ÎlnV¡í#rãÃ& ùb_x[<»ÙlÚ6ø,ç×ØfI"ÀåØt#%JeÂp¢ o% ЦéËÀõ]:äP ¦DKt¬>Ñþ !ÝSÑ+k½¢ìcMH²éR0lV3¡N¸Q)öñq6hÔ1Xc0ª(KÀ}yâêÓ&ù¨#-Odi*© 8ɺðD5j¯DBQéhU
býLîê½sÏ5êfÆجÑel¬Æë]C®zOöº7dæîK§%Íâ+0ÚÕ¶IÚ©
µfAq&åÿ¯wjGv,Ûy§S{zÀöþ£Hî&^*SrýÉNmý£3d6÷*¶m)sUpqß_
áàtªKÔQVU+íýþ¨ÞûÐsIW°k¾/°@Mó«u»4QÔÕejo½4`ã5#.8À
D44=0Â{½ª#%¶84ä`x÷xh]bã'U+Ž+Ô¶ù*÷kF·)[C0ZØAIT#-#-NI4ÆY0íé*{Îfòk´½úí.ô¼FÀÉL˳$ÀÅ¡¡³BÛ¡ð¯Aà7L½ñß÷âQFÙp;¢ã£7A#.jI
)çh\í2c,`ËßÝDA4>Ç'ôsçE½ðî#-ãpm8yË)shP»=9àØèÓ¶phá
ÄïÏ>clæA^E8j¼ÄC2DøZ;3< ¡Ò'(6_ZõÁÜêãOí1 @zÆWDèòz?òÑÓÔ^eë%%öPõéyQòPÐÛsIAb«¦å-ay®G!é& e°i>Ó-xpÛóÙ¶G>0ò`÷̸'zɽ¸cÞßæ»a Ùið$¥*]0rtåOCäPð'oÖá¸|j:Ä1QÅ1ZNKB#-+x¯a¦ï«¨éÜkmKS0)T@|Á`LúzádLnP#eºÆ±×$ÌM4hBJ¤O.\ÜßbØ0Bh#.Â"!(¾ý0`×!V3 B*EÔ.#.#wsÙAô ¼Ô+LÄÐ!ëË_sÓ`ªÉ2¸¿=1eü`ÐÓSȾ°þmyߢ86Mö\û6¨h¶<7²9'÷ä^Îàle¢´ï+ùúLfÕ¬¬Uû¦÷ùØê\ðÖ#%^éú:¿&ûlFK¯áõÛÆ×)%"+E[¯Ü¤"È¢)"?Ö-[EP,()w½Áèï¢àöX!ã_¤ðŬ&Õ¡íY[¢X7ºy´íähR&þ¾¤ªy='¤âøä¤(Ã$ ³jC2I%¤Û*+iFÓJ$¦%+2F¾}ºbdÚFÍ)J²Êbe#2( ¤M&
|»´Ab)E$Ê#j2M£CII`)3&-¥-d&YAHÉdlÔf$&#- QEHÆDÎ^'d#-véS¡ÔÊ_¸([8^!¹íUP£ j&ä#-«ä¢dÄıL`8îã:B(?3£Ô`¦~¯ÛÚÁt^Q,Å1½%ôã#%äVêc¤¬5Þmã¦=ë¹#uÌ®AË¡¸¹óá9ê¤ç}/½lm×®U84iOå½#ÄNX#ÇÓK6%ý1a~ööÏèQ1Ù*mm8`aQÒ #-%³Ó¨¾ô¶÷Û}¢2±6R´ÖÅi#-ifÌ"*ÅU;R¶_DÂJBj5¬fFã+[¼çpÈ·¯Í-2U¥¾ÜpÒI,!ÎÌ}Ùþ6Á±ïQwo"19{áÝ@Ë ÜÏnÓÓÖC½gØpã¢ÁDnäL>dDDÜ(Ïãv÷ÞuÜzÛéA,cÓ#%Ûó·Ø/fÀ¾S²@ÁµHD4Ú&F«ØÓéê:oÅתºá=ï;ÞZ°y<J6|ëØ·wÉÚ|Næ¸ÅK§G}9H}Ð9òÒ{~p¦NJ÷¬SÉs!aUwn`£õ%<{ºX¼@ÕzWÃ/Þ.oÏ#%ý¦k{fÛé»ß;yñÂ)yó.F°üí$×£ sHÞcDô¼Ì.¤D /ª`ßu=÷k£ÓÆ¥Æ|òºã¹1ºg\¶á©ïjê·îVànÛ^#-»ÕÑÇat©UTpnù×D-ÇÒjV3Ã~+QZ#.x/ëÛBw.«ËÐJ&`Q±¦Ù2H±<Y£,mêȽò1G·u$#%M¨ù§RnX?Õ8ØTý:óÖ¼·qï¸Nþz#-´@FWx`ðóÔÕt¿FÒðÕ®ÉÐ4æ×éÌB©B#-9ÞÖ¡\Ì;²#%r Òl«ÑqåqE¤ýNzô8Þ#%§ÜÃ"h#.ªÇÓ|mÁãPÀlÓT¬wäíñ#.#%g»é_«|:¦¨w¤II)>m ܬ!¹ÏÐ3¯ï²Z3b2øî,fbïEQÐÙ%*êcZRn[NeãWÆ8!%p1$ÂC5bêº_ðc^ëk 4uúIÚïBÅ×S#Pù@dCÉò#X3N9%ciêÖáx©ËHaøfµ'ÕÞãTâÃy¡³&MHFN&r`âYÍH¤(ü\!¦ªÃÑoñ1BEà¶)r]~}Jô¥11ìq*à>Ú¡éÒRÙD¤Ù ßw¬.ÒÆ£uf`Ái¬DÎÓ«ü¡CÖ%ÉÔ¾pbf.8`S#ã#-B`è <Ên.'
(Ôîöíæ¬So.üLÉubs<ÍÓÀF#-ëÍ´ìY
0#-Tì¼E£\²:a©t®8©%¹oØ-̸ۣ
D86ñPuÙZ:ÝÒ|í!s¯0ø2ÐÅ
²è;Uo¢6S5o%>.86aå°±rĪé#.ûÚB ¡¦f®Þaçc;=~sS'\â³>.¯7ÅÚ?££·U¶Ìη|à2ø]³*ÜÄèì1¡ Þ¦B¾®u»#-yo¿UƬ|;\ l¥¢=GZ.1]¢ÄÛ-#-ºø'ÙI1÷öBÄuóÿ¢t8Ãç}¶Kg79ήXð/-ö©Ú$úï¾K¸©Ì¼`¬^{¼ó£dm:ZDB7Ô\r]ãpäiÊmÑxºËgQ¨ÁÍõ6pBÎ{q'¶:<9 .Éohãì½vw~íÆÄìûûä¦0õÂsZ6êdè¡Èá¢o"ÛTå)§tÌ1LCº}Km#-´QfÍZúôZµa×T1V I{!ÅÎÔBÖ©ÀäÉõó6uhÃ|R´zDj,7wÁV¡&ºóä¦";spprdwss #-Þ·0©´s9LkNh2IgXmIÒ¶öï³5ÕPåm©5Ë)ØRñ©PÓcCt$.436è3«Ó¿(Oâ33]2"dÜ®7î)°È¢.Q¾öVÆÏ#-Æ©\`9G(uÈ)Î#-SËÌ>ÎÆ+Ct7Éf,Î KmFÉ¡VÂG#±ªè½åËTµ´D7NÞóf´¢ïáAl8ûà×*ÆîsBâIcdM²6´ó¼3I±#%[N( ÓáNt¥ërõ£f½Qi¦d6"ÈÝABÈ¡bÓ<»T.6BÅ¢IÊmÞy˨äKá¹S}ó¥Ó©XßµÉ7dµ(p3!:¬ViåmËÄXÁ-dðçã2\$d!ãÆ®£q#.;Íêïã³IÉ#-ç?H8i÷
½ñlc®6æ9ñ¸#.ÄL]Ê4IAqLîò8³QÊÞ1×Õ6¸â#-âLÌpwGh,¬TajÃRÃÂÂõ¾ãuÜÌ3eÖLoÓ ¤>q¦æíqÁÞ´qèÈú¬v(û²j>=tOKä]»äñãäéu.d$ݹ|[¡r@÷ÈøßJ±Nyf^%Ülâð®ÙMzózÓÆãn4Êâd¥ÀÆ#-Z¨9½XäÀcpd·Ìqè±´Î8㧽Y6ÛáìÃO
ëGØÊôcrK¼Ë¹ëEvn̦c+%ËZ#f-ÌÖµÛ{hÑjpë¬kG¤dÓGVÖ¯eG-,]pÃk:#%NöçFÀìU½ Ïz#.:e
øKO ÖÞ#.ÓÞ]kvÈÙåÚk|ÕÉ
:ðVÓÛàgEâÔkhy´r¨lÝä.ci 53S ¦£ÐãTa%33ê#jSE¦ HÎoNKJ¢è§FjÝ|t0âqgFÛÒ¶øÓU\¼$ÖJÅÍ¢Ý#.²#Qkpz0ëÑäàH8±#% Q¡øQ1Û(KÇÊúôðs»¨hjX#%õM±,
ÝÔh8@»êx4× ÈCEíáÁ²(°ÛÃpãnñ'®æ3ÇnBpJ)#-ÄßV9$Ë#-
dI;*
´
±xâØIhgR YF{±BF evæTÐC#«WT:Lxö8ô00ZEÉαÄi£Ã
Ú¬lQ$#-M5#-°fY¦÷d`m¥y,«¥a
0X)5OÖ¦&nélrÌ%3d@Ý''jȳSª;;j#.Ã2äÔré3Lr?H0¤F¨]%KÁâ]F£^íRjUDv¹\ÐÑ}´¢#-#..,VkAÃm:(ËBPnÒÞ#-¡mhÚY<¶Ä×2ýë¤r´4;¨¶ÕÑp"XsAóJDø#-
8k4Éï2½rëàÑÅǶ]«XmpñLÍd2rH¡!!(·Ù12Y§Å.W~Aæ]zTÖÔIÚdÕJ0Ç5U¿=¡]GÑ9Å#PùdÝñïØ ¿Ó²é#.,u KH "x'FTÍ»d/¡ÃrFs0p©ß¤áA¦ðãÖïÅîÈê,ÊD£ù*%;@¶Ø4ùz3.Ýõ¡#%ozc~sAÈ,((ÔhåâaJ&«2²IÍ4Y¼Î¬|9´¸¼¦>ÀG.é&ÐC{G3n,®ÀÚ¼P]üàå'Ýá2ªHz ¤]aµÚæp£x»I#8ÊZSʺc#i·f0 @q58`¡0J6MÍ$áPÎQUÂèP8£¤Æòlr èMàXÔv¡»#%áØ86&©N¦zÝ7nÊLïÈw¶nMÚÐÛHÎÛ²9ᬠ3$K<H6uGÙ)q#%&0cJA#%þ¶ H'èÞP¾,²#.À6¦ ;RF )_ÝìÈóWêñõqTMÜ)ÞR4ÄIfµ/ª¿oyí^öZ~R=e(9DH Ðj]"_¡iMWŧSf&T^þ«ßk{m±P.¤ÙiZö|CÝÒþnÛXÓ1Ï+RÃÛ>Ê#-µ´NG:<ÿ#nß7È·áëdù{©´"êzªiËVï°¢Ö°äN.u&±iZ§äfj`$mÔX®GHrô:0Ò:ô.®rD!äuíUøÜÍ´üqGç).P¤IÜý\¨#%¸ÓD¡µË2Úüi5m&ó)6ÕKY±X/"!
¨§ÏZ4#¡êâ# ¥EµF<åëÈH¶É°ew»¼Éå÷ÇÊq.H¿ "ìûöi /âh6-!#.FcI]wfstKÝ/å¯5?x%ØH ¤Z÷(@ð'ÂÒ ¶YàíØ Å#-ÈÓù°¢!"8r2äqcì"qãK¾5#%:Þ/°þæbÆ~¿$*ÅÏÇ¿úÌÔ¢ÿô¶GÕöäq?Õï÷ÕÁzó3¬¦Í1¡.VúY$Ë)S4·æÛÇ7|^y¤6£Do!,¡xøAnEzþ¦ÌMvd6cê.ØFz# Tï%Ì`ÑÆ°0Ü«RiH9[©S£°æÏvi{#.PJÁ§¼ûØ¢êò°,¶1¡ç=|ð:HªI)[dÑ´¥hÚ±¢JZfj#dÆÚ,Êù_#.Á"Ò×xûÓw¨Æs4ÓÔ %ºöíèí\ ÉxG¯h©õn6v#.zçZBümm.F>ÃAV ¨ U+)MÒÙ<¾u$Hmï:Þ-'|ÑMßw»J"~dhÂ"ñ¥0ªæ`¥´=*¡ÏE8êÛ¿K2ZªK4aκ¨ºÉ#-»k®êJ²!HX0Ù
¤t°TÕKo?1#.D ñ7Úàpaö
KOçf΢wsøõèi¥«õ_Û©CM\AĹÛcÕâA7¶ªÞ|XÈM®ÞqxMH"´ìF¹}ÚÜR»p¬E#%²k(¦á³«©*é@¸¬XZ%*,Rò#-Ê¢òQYÜ®ÈWÀÌÍdUÔÆ2-QBªÒQEùö5¬¡ÀGj4ÍÓKØxÔb7È-âE@³]4¿Ñåï|·É|Ðhðò¢å\$$aE±4].z}NB#-×"ëGÚEµ ²^*¾û!º"j0`«¡ÄÆi?s$÷îß±ÏX£R/·äÁf+¨U~WAj0fP¯ÊÚjÑ#-7£aÍ[ÁÅܬ9kÅ#-bülê6±7x^»ºB³XÛ"3ùâXUZ-²h%°R!½R²k¬Ã2.
,ÈÌýÞS8zPÎUaa5#-Úï<f6]s0RÊ´<l<5½(h F]ÝNÀ+tü´Ó«ùµÑù|¹9êΤ<ù,ê»M7VÒºpªpLØ!Íã¬0i`§l32ÝÀmPn4¥Ìç¢
Ãh78§§MF$¢6§gër¤l @òÝØh4YO6CT¸ê´a]fh$0N¥8
ÂFS#Y#.h]BÈÂú#%EA&hê06{Hc¨7ç´äÃý9p"ÄPÏw#%²ëéîôÚAý>«U½íÍ`á`]ªQýÇífoXTZç¤ÒâXÓåÔк&;B«J¾èº~ræèx1%ádçñÔcX÷×yAóGÙæH @aEUUT* SÁ~êQTDI@Qòõ¾P1ésó6xKE¼Õ8PçïjÔo÷ 7Æ)N.6¸têÅãü6ó%-uC¼6¥À×UÔJåAw,é§nàïÓ´ÉiµNÂԫ¬VvÊaå8-§Pî! ºªjo© \Åúٽ¨GP)D1¾ëpÝsE¸'q¾qCK:ËJ!LCÕÔU(kã®1·AÍiÖ,Ï8Uhë¨,°ÞÈ%°±cgCÃyLJÚ ;¡kafKbñ$i¥dáÍú;ZÓ}å÷FÓX"xéâdnNì°ÉwÌZ)éC7J×T¬CÕKÌéöÞc.Ô2d±:C:ISØ2Ìæéõ´¦åõ}]Ççf&@bÔÊHâé®ÌJºm°tt5dã8<W°ha¹Mú¾f~.u×Ôw°óÜ»(¨#.) Q"H*$dD2H¥OiÎ:{Kµ¢Æ¨¨Db&³YÎ$Y#%»)` ÑD¡À5#.s@*eO²eÒx³¿ãeiò½7vêFÛkâouÉu¬°»¹.ÝqÎv½NuιØwLuÜe^QºTÖ®ÒV#-Êî»wvÕçt&jv·^^kx©\Í·iQlñª?~)8pÚ6j@À6le4Òf¶+5(¦k²ÕÒ´³KM%eLÙkcKóþoªú}íîÓ5£R«IQdß~õýwÙu£¨t¶¤ZTæÔ4c&AÙd°Ø(P*'#!hÅ@º ÝTé3Cm¢/ ð=¶+ÏIç«Có0¥o-D,¦§¸ÛõdòÕW°sÏT(±WÒ+òBa8Çw»3&s4C#.ɣMI±EP KÌïN$ãg¿¡n`)d·(pÁ~Ãñ~;dLF^¬¸±i·ðX#%´KÆ$0RC¨¡JìÛ#-ôn¶é+]å kGíØÛ¦ö_ìÖl!g*?3þu#%¢%4Ëin©¦CjÙ¼#. æ¸ZÔÊf/D°ª1dm®Î³ö³o=ÔCÂ¥iÃ#M^£EË
NYºþ7ly¯Æzë#-¶[2C¤ËS/ëä]i{% ^*A.Av¥~@ÁSéu#-².Ó=Á÷(]j902ÊÈdrpÌØk$аÉ?(Ôë5856j:Õä§èwZVKäPæMmãÞ¿`?©F $#%`¤"xzþÔ{þÎj£S~íå0v#%BûN4Þ?è5?veTB[ÏÉí«WÓ²3Xµ#ÒYM×vØ"Fñ®h%Cyv¶»Kf[IfÔË%JT¬eVIL°¬³T"¦$ÔQkfKÅ6Ù»ºjÈ=³*õÕØ¢¥¤«STbÖ*i1RÛ÷·UòrF¨ÒMjƤ¬Z«dU5¶½ÕÕ-F©&¡my×h2ËMdÙ«#-VU5½¶¬Ûb--ÑiSo:ºx¹¥¶a®UÚÍRo0ºÍf¤Äµ¡*ÜMq¡!#-û>)Û:ó_*JÂèÇwêÖ=0j3]AååâϦ¬Ê먼h%·rÜÉ@÷tÊeÝèzQÀÖìYn~>ZûC)L¨Ñ jòÍPG.J·Ý~¢Úïµ ¯NM¦VhkJKlß7ø>ÞÚüK[ݶB"Ö¯enÂÄ"BÍ#-#-EÙT%DD]bµÐ$VAÚXáe#.
ЩA6Qa_\ÔH@SbnjÛ6Ô¶j&ZJM«Õ§1i[)Z´Û6Mli¥4RɬYb%¶jÄÚRJJR&l¦eÆÙ(¤ShÔIdÛdµ)¥h%1Lj$µ,Ò6ZÆÒ2QHmbRÁ²ZÊ#-©$¡e%&Rf#$Ì)eª[Û+AªI©Mi¶Vµ,¤ÅS%J{j´Ûn³jƪ£keeZm&kî6ÖºT¦Ûe+m%ª·³W5FÛÌÛhªÑl¥±¾m¶åª¤D Éhpt#.î3²pvi|±XÀW§®ìÏnãi¶fvã¿TTý#-w¤»#%äd:´9p8BÒúéPi}ÁësÒD|!$I|nxx_¼Af[Ûr¨zå êEªÄhBD kkC"Ð(w&§¢+b`Pãkf2û23Ít*o¹7 vëEIàî¢ÃVÞmìÒÐÚí¬!«ùT è4DÑÁÄ¿¨ÅH¤æYªÔÀC¸#.y;
V*©e>À÷{H×ó.¸@µ¤²+¹À{ÔÁ.ê2æ×0ùf(Âÿ/®¦@ i']ËÎ&òfqÌ>)ºÁ"Ý@â1*4Mf¦´ùË×,eñ§Y¾Bé[³]Nº¥ÃÁõóìV!Åp£vã}¢H]Ü}¢lIçÝòÔ»\Lv&Cê¨}UE?*4D$ëñ(ÀOW~©Í`Û$zaÄjhhêr8¸SFY*¼#%8YáØd·òØÆCDlª8QvgH¡¿Ì¸0ÜgPêlhvú]h#%è&` /Å¥«úþÉïêÛÛ¶ ˸#%Tj£[c#%[}w][S·Õ¶¨|T1ÐVcV&" Æ6âóÀÌÛkïÞ´*Áô}N;{ûÄ!#Ãa$wÜq"v6"Y6íh'b±ÂX`ªk¥ê#.¼JK9÷é²ÞЮÄ_Ÿob£#-<ìÖ 1ÖÒ7> àÉ&°º}XT$A\³¯¬ôÏd 4#-ÇûÞåp*°ñ(QAdOÒïrf °ÃDYH¯']Ñ\ÃÚëÒþCRVJ!ùð4B%,Ì¢Èm²lÈC$h££@D""lÚ@¶TG*ä©l2~ú4j&#A©hé.F±Á§ $#9½iPA¤À*ÅY£ ×!¨âli0µÅZ-mpQùØÄfÔI«&àbãÃÁ¥o×@e]@#-âw&Ǻ@ñ¤ô´³qêÈ@ÿWz&nøùãƨ5"ÑJØL©ª5b®<.wÀMäR)ßÓÂ=}´~åäÓí]XQ2%vªCqÉñ´ÔºÜqD´#Ù#%¤"°Á /LÐëé/Ý°?«½[@Ä63ÆZHøòl¤Ydhã%]%Ïv(JhÞÀ£§Íç#-G¢=4UdÝ8±Æ
7èW$ÚiK¥éR¸¦ñb»æøZ3ã¤08XZê8«¬]#.#-kÝಶð·eÊs>6×m·½U¼ö1`ÒFí}JRqÜ[*ÊÍ
С¿qR¤Ä:üÜP(-#-PrÒYõQÛK|²¿t$FóT«MÎ
~©Ç%üR{ÕÛ©Zdj5!&K ÓL@B`X¤Z#%eq*&i4ÛÕEC²%X3°Ì8[ÐÌëÓ^ÊØ®i`"Ü"Úå²BÚG"¹×3 N¬7|âóâ.Z«TódÑ- :1ETR 4'HÂë&¸SJÐîSraª:ÊSKC,qãPë¨`jtaß½»}DϨÝD$#-M#.*§ 6©Ã2ÍU-L¥ELQAë^ÍÛ²ù~]jñJ!³Ý<|ȼlèhÈzô¢&~³ÖF0EJ8õ¢Áºä×Ä0ÅhÜñÂA§Z¤;ñZÔXÃNÂwÑV6iX,&Ë#.*#¶öÖ;]ÃNÿnVð!ËnHC-ÕöfrÂÙ´ÏøãfFÁÁË5É j3 duTI˱_
«Â{8Òõ¹BæZ#%vý&ìdík8ë$,Ê"ûÀÒç3qÄ$S")J j¬jfSÖí¢¥^êd@8QK«A¼G-&ljàØ·/l~#.u#Èjñ7<®TI#.TCêkëú^\±[f>ü¹|nïºãÞÓ§½w#S^ÞÔUÖc0°sA¼ÑìÌfH±TUW»åù^jö^ǹïW(4ùªì÷oo5Þ,"~Âêó´îF"çzèçWAW&StºTn¹¡y®õ¼»jËb%*N¨ÓLí!îZ4Ú¶¶ÔÝö0#%*ø1cÅärO8¡ß9-FGÎANýì¶3ܽÈ:w_t7çß9urêíw®ÎNÚ + Þ'$ËGÅ®ä qªñÁÇ&D¢GQþËíG1_Vå5O\¢ü¨<O>Að¾Ä®¯ÎÙâõnAC¯Nì¤)CqºÐ!~¶ÞTÃk6ßsÕ×äF¯V¢-çpA'#¿Fw7JJ
óÄÀ|Vôõ×ÙÚ£Y5iíÚ¢îë±Ò¥1Ki»²ÌH2 .RÖE±mS׫·G²ó<sºé3Dh«j¡)¡FMúôí
Ü$CÒ#%¢ÊrêÏjìâò×»317¼GS[wõ»øÇ¢HY+jÎÅ÷çY½ýyxyÝ&½8)
AI,WìÖThTÿ5¢,Ë9Y4ÔL_<Ñ$ªñ³LZÚÕÊ66mÍ×-;®Ú5|&µÊßÍb1bMW×-^ÐÈJÎl,PB¡HIýïóªØ$#%H!RczÄ(s@y²5¦{lzPm~MÑøæѱ-hH) ´âgMtÌROÅ @²PsèÑK±ðÆ3D2¨Pà
#-¥"YC¬;LÇÝ°äCî>F¡àØDC¤ú1° ÓCÖxõZ)6ú^#.^TR ÍÙ$í±^»¾£[ÑÛVÒôYMì5qCÌmÛÙúµ¯î>»5F¦kºêL5"B)")PiT7û ä9#.àEDÇÃ<MZ%ÜO1|c<_pª#.¬AaÑÐ3²o£æ¿ØMîT9êH?% ¤67^KÙæ§á»DÈð¿Ù°¸°ÎmÀÂ`h`Q ëø/uR(AT(þH¢V½6~ëôlߩݺ(õPd$âÁd ùýÐ>vãi#%3dáút6¹6g|¶O³éÓî~ëÑ>¾×ÔF»cÝ=Y½W2¿·~ÂߪmeÔXnæìÔdDo¿!æ&wÙí)#-mû¸úÑÖ8;;#%PTU|F#8fôLÛ0ZÐÝzYóÁg8¦©GîéëX1Bo GQ#%æ:Ï)ª)±§ÍøÉèÂç_õþsuNïbxPAd;ɪY\Úë-¶·Jª¤®P !¼A±f
à #%hBÈ.j+)ÆQð·HSm.Úýh`ýJ0b8´Ä`1#.$9®:TÓ7±êÒÚÙ»ÐaÆJ~¡Méè}½A9¼ÎÎEeþ8ô¾ÓRH H©Ì" ÑGx[#%ð0Ï}¶|ßTd¡Þ}mdAdO{-cMåÓ£)P ȵ°9È?Sc¼Ü¼+£[üèeÔUuO·Â©íH÷g/×Äõ§Þ±C»¡@ÀvR.©æ#-ëÎÈQÂÕfX-ÉJúh)/慨([¢(¨ªR_óÝÃ(ÉTÒRÀ%ÔÞK^/ÖkÙ¿=·y%oÒ\d)
Ì#.RùLjí#®XlBÆ6ÚlÑ9ª4.¤)íYtE¡I&èR D8qY¤ÇR)Tc
2ÎP.T8mî\Ú-½Jø*å%¨ÚôѦÛÆqkKX\Äé(©Áe$±ÍRXR¹§ÿH@Üvabh¹#-JRÁjÈêñ·#e¤ÙDÑXa UÅ͹ÅFñbÒJbÉiwÖzòóÁºslsl&ÔÉc1Ã
àJ#%Ôd¡1ÀÜfs ´Î´ë Pd]¿³¬ÃL°Gk*:Â7¬Ö0Æ,fÚ;9»Dô#.¢`ÑaÝ+<θ±QSj!L#-¿?ÄwØu¥LdD Ïå,v9?äKRääʬbö+ØÍÅ#&%]lÓ2Õ£2lÈHÚP©VªKĤ¦7F%¿«ó¥#-ÃTzo[?å,ÜÕÞ 0©¨Uõ=ÇñËñPÑ-"BÂý@Å%Y|ÿ_³+ZuÅoV²ZY65¼Ú¢¼êädÚ+¹kn`ª¹»VM ÄÉ¢i[EDØD #AcÚý).lÎQhH%§q¼ú®E_Õ Aá,4ú#Ú^Îì©M79}>Tôæ&¡ã°2B*´U#%4-ÈT¨µ&´X#jV×ÒP¨&¬¶îþû/U¦ª[sßÍÄæ*Ðy<âOÒ)!hËÁyV*^v¹¦Û=ûxÛzZúÚ×ÞδI¬¬Ù±t¬lWUÝQZø|/oS·k³bÕñ·im¼ÝÑE&ƵI[Q|ZÞíá-¶À4Ò`SE®ïj°Go³?ÆSâb%Ò÷â¬Ï½°Z¡*u"'¢9 @Á, &`ØÃ"Ë!±'Ii^x`ã¤ÊG¹p~ÚáL``D¸z@þ0ÎáÑ4Þ&(ã"pX±}²E`fqçÚjxwSnúÐ^'ç=Ï&¡;º:¨+£§§ö<ÝAÒ»KÖ]Hz~¯¼Ìrz¶ÇÁ¦ÁoÃ9̶gP±-`¨=±R ©úöC[qõtDàéÆ6ÜÉÖ£ ]Ë«x=aá öSȯp¶#.©QEÒ)Z6Êif+i¦²Jé5%X£Q Q}pPïgNä#.ú!æËuåRE#.°ÛSéï]çÓêï(FèüÎåèyâñn&%ôgö¼hGÉD{õ*TE×45£O8ݦUá0iPP2ðÒÞ5ßL¨ôÚ¯rDQTE"ÛRØ bR³jl"¨]°jª ·%A°b9rð÷ 'íí`u$M#%A¢íßW£]ùxe½|P¾ºÍG*·.ðÞ´`侮Tt?»h«á¸×=N$~ÛfQÓÇ+¸}9
Òé¶#"PøDÖÍ*Òih©£"CÂÛ¾Ó»mi¶~f±%ÈDiDÒ&8a _X\T(ó*/ÇM§.SÝÐk=.ÑÉ°ÿG4*ǦkÍÅ?æ
`íÅ¢±lì¿Ãý)%ÆðãpdLz&Õ¦&-è{J3±IôM]@üÖ"ÇÞè=× Cçà#.vã.{âTRE¡jM$¦EjjÓTmF´dc3_ı&¨©gì«\ÛfeSemS)Rµ1¬Ä¶Ök6ÒkIµbM´[#-fÚZÒ)ËMkb±¨2³å~þþªùöMC~ÓIË~l¶4HDѦH/Ö"¥-E«]*ª-E¶+vVÁQ"2Aü E&àÔ=wNüÅÞ"Ù$BF@Nô#q^HÙüeÊ ¿ôî "æUF¨J-çißE>%0I=ÜéÜ£vÒí#rüè+Ý7øÝLQã 5}P¹Ó]èzSYý AÏÔ¿Y!ª¥<ÐJ"TZ~Ø ¡{RÝ ET¯¤'^Îw,Ý|§î̸ÞmÙMìª#%½Óµ_; 8"¨;B$ã#.6t &0Z`Áâ~}ÙÜQÏñCEK!íì>ãðõ¢'j@kÐá='bQän:eU6âh#-¥Ãñz»#%Íáy#%"#%n@°Æu®Ý§Ç5L¨HPÅÀù}aÁ#-ø:#Ó,¹v6ÔEbG#1(~úϳGW!E¸±±x<Ïïí³å£ÚßùK}ÑËLyjÚlÔ(ü±óvÍß碥Bb!¥néÇDê?X«hË"H¶1Û¹á P¶¿=q¶Öü(õåMô §Ï{¿t)&ÍßÑt×Äùo#.m®á´Qm ((-)>¨ÆC%/KïØÖ}Ûþ¬IHf#.#.w%,"Åñ´Üû·½ý³Z=¡ÓC£¡' êÿvìI¡P;ym\ ¢É06Ï)Än)i#"i¢¦XAÆ)u+ÝRýT·T°Qaâ±qÎÒº#-6{dÈbAPÒ,iê00@#FÍîá'>²ÚXd®Na,
ÁGgQd± ükO˶_ã(ÛxÐH13¥,èC¨iåéU%Í×>Ý7Äe@ÒgÔé
:ç8_M9ƤF5,jÚ÷N¡Ft''¡/\8;2Ëß©V<ûÃîÓ]MR&ɦã³4A=$G@Àf G2+
SBRu4pR«æ=GG+ Ý#%CÓKnTrÈ~.Ã%.´=.÷Æ3ÆTd<BR8*¢ÊxÒt'åLü3Ü#-Â-ìNAô#.÷óºæóÌÊÙp`òqa&X²1"Ì%" e56Ä}#ôÌ ùB¿kêm*)_]Õll»^¿§lÜòMçAsÏ:IKÊêCõcÙgÙ@ÊõO[XÑïLÐQB´ÝL©Öhc¹kJ6Ó[ÅM\59.Íllm'w3M¨ánFE#.ímàéE"ëPÅÌ£cÌãªvOÒ _>ßëù÷«ElûDι ù`*àÌ%¼&hÈf1ÈNÊt{Ø÷ä5"#%ÃïfqÊsúæ×®ëå'¤{fóü¾u¾¡ñ~½×Ne#.²^¼¹BBj©U4mÈlªC HÝoDci6l½í¨6ï¯R¶¯ÍãºÏ{¸H.\Òé¯@Gph|à4öâtZ°he8h#.pÁt\³ÀRa9ôØ1'"SôñÓªH3Kg[uÖKI]êq|6of6áÉP´èÊ?Â'7$wcåé®yb)í0Ù!S(óþÏîe¹'ãFCAÔk©#.bUCÀÅ`Æ4#1¡*Ò¥hàQQ!:` Ýó÷Ó#ï"6"ªÞ©×õ¨Ìá»yIâw6UÄÎ%È0ÈõØefËèð9´t1Á¿§ÐÝE?¦ N0æ§NäfE:öà¬ÂÕUÁÀ²bÆQ'àÑÇÙÂ5vy×ÝÙ#%=²ÖO_AÕ¶¬Cî Y¤¢ÚC c¬,£a+açV»U%V£VË.òêK(
JcB Lü?G´øeÁþJCwUlbú 9¶Üè-Ts»ºj´»¿/·×+¥¦kzî¡Ä«fP%¡)fjTAÙÇbøïVµ®?¦¼µV6"´Çz72W©yÅAjóÊM|QMã#%ǦHÂɦ"N¸Â0SBªE ¤L2oDÚä2Ä©¥gÕ$A¼5¡Vn%ÐuØ1§a.¡N¯IdQ¼6¤H¦Û,Ø`E¶±ø¶46iôô#-Q¨ªÑ)o«ÃdpC(ç¹O( ýDí8 äñY Cl@/È&{ÞºïW×Yçÿx6bKü?~ÄÄëùOÛOê|*§# Ù1EðK|e
¦%jrôt B<æ½Ù*¿#.uiI¶fÔ¥£L©Ú*Ú¿*¿2Öüh×kNí\±®ktÑ®W6ÄçÒÆðAºáä#4Ð7Ä3-Í êGÀ9n#%nëY«!PöMH§£»´£Ñ¯·M9ª¹sÉåÆÝN°þ@#@«p±0Dâ1º'Ï¥ÅæÝâÿçi°ëá#-·ÌR4wrõO²«$Ív¾½¸%¶S5Ýß9w½Û¦»ìL&ì}fØî+®~c;[}d[u#%rô&qG`ò½TDM@¹#-·M÷p^PãNé¦ç6áÓÃúS<ª7¾3¦))i³2¥&ãLdêX¾Î?E_Q\añpíÕyo×#-áå°úø5½7_¸Ôn9ÜuØdñÃÈìa¡#- dïH|üóÈcÀ_²aF&½ß ö#.çé@<ç#%?Vaq£cêí°}y0¨d=¨ÍQ¬J²(Ü(+î¹%LûÓ3Éa©<#a}´ëFÃR1±¢ÄqËÈûØ,*C»qÙõÚlÃA+}PªíU³¢¼P4k}ÂSç&Ùà fðJy0aX¦I÷$
íBnx®{º©³*æÐßøÁ=ÒÜǬø·]_ckbúwFÆ×¥µÂú5-#.8´(RP/ÚTH<þzßýPÞ mÜí]+gG ²äÏó|ÔKwس,F1#-³½m";eJFèÈ ØB9TÆ_X cð% èêniï"m5KyÉ#AsÔ&¾#3@!¿çý0i8òQA%VUk°½ë·¹n+ê=~ìÒ!ßçÊzM¨¶f×Á ØO$ï°QlX²¤ã?#.Lµô®9äáFxiűåªØõÓõ0ÃLw<ÕG4N¤qîTgîõüÛEF^IÜ$Ã{ê8Ó)B*T3£myo<׺µ-Ýjëcn×Ym4ÛLÖɵ2ZUlʶê¶4nKÅÉ5ÛjøH#*"«\Ãëu!çõ~#-#-?T.¥IØ)ÇÊ¢Él¥"À×Ú`ú41f¢RU.¿KsR{¶Ö·>CÓ0|}û<Åà¯cm©Û£P2ÕÔÂLÉ_ðÒà¶0Ý%+UPͱ7feÝm¡ôK1;ð@! B±¹Aéý\bðqµãÊÜ »qFFí+÷ðº¶vq|z2²WÚ¥ê=ÕØnÙ⢰#%OÕê· È7%Ûú=kc'i:^Äèêò¯ºÆÂêÙ!;WÄõp6á^})¯¨××ÙÀÆÊÆ0T.¤î§<@ÆÚ¬ý×iphÀo:J#%çLLyV"º,Ö¢Ô»L¥¶Q)}·÷ÿ_âÿƶ´þôÆïãRû±~Ié}pÙ2@<Ø¥It²ÜBã|bìF!¨AÖØqp)#?1«{@#-YùBöÜ5¬¥+Éo`PgSPÕÎ5ÐSåV¼J¢;}ÈñUw< =ïÃmϤgwP~kÞúiLÛÈ#-ÆF2#%ìâ¼'ÚÔ¤#p«é¥â]#.çÐÍ õ2-9ß[¥ÄÁÄ7wo¾U *y<20»Ò*íØl:PJe ÄyI2«Yi,
æLÃ#ñ<E类¥øx¥
÷w¹`ÜtÉJI&aozÍBO]ÓQaðð)G´Hv±ä:¸;¥?ËX,)CÁ3Ùûu¸òÀe»5 ¤ù9/ÊÉmO¤¢9(hßö>Àe3bXFÄsYàÞ'o{&=¨Ðű¡SC
ØÃáFßµÑ0'ÊÕÀàÆv°î¶àäb V±<°ófDéòÚùôãæ\Eù EN Ô0öÝãt¶:u¹2C«#%Ý#.HPõH° [#%m[U©i+mxæ$¨)w
;LöÕôó·)áTH±UBï6· @jpêëg6TnYì=AE#-ë+Új«vHéY*é/¤S[jn¿på8 äx]4ô>aßËz0
¶G0I¢¶4Æw·@sDÐ&G¿`w¡
Yµ3·û¯ÎÝ*ªF6=\5Bp7ÞÊ
àÃ*ð¡(ñ³Ð¢n0'òËDýDÃ¥pëSãÛw&úÏÚrcÛf<ÌeÞYQ[K0[æ]k9&#¶ìÁ´6Vðxø«¯R×q(óW#.ëFqz ÒÃ!dÇI{#%@àTÜ^TÙsI¬;°Rz7Ö¾¦þ¯±Zx¶!g1V0éý2Ñ-#-R¬Ê4± AH÷9û¡J²(P$¢¶°Öµ Ãçú̵ d¨º±Dð²ÄÒ$ÑH;3ðúý¹Ï9÷#-ü<ÉS?6Ý]gWwm×v_/{6 Ê#-1#Ïâþª©5$rÊÕy¬ÑÁUsF>¸:ãq×ÙTn{Ìý_ÂíÐÐåU¬9Ä04õòOeG»ÜÌgǶ[#%Á¢#%BÀBN;»1As±Eíp¨þþo_k3zü#%l;ª»hM6+ùë;rîË#.ä#-«éí]8_ö¶VÚ6Âk#.Ø°Â6IæãzÎE¼|ѧ³CÞãÝwNÅ5Ýzó´ñÉNxU¼QQÃAI¡hDc4´dQuÝFÛ®ýw½ëoZÚ2pÀÆ4ÜMj¥B±¦6&8bdjX,Ø8%AR«hÜ
Ê5¢ªJ¨)D0SQAù!CزÙr½rg×VµH}?§@±ª d®Í¾¼5Ys¤^uÔ»wSS)c-ÝÙtM¾/^51`(*ÛrÂJ,+6!J5Öy¼í¼ÙSe2£5%¥i¥±%1´aBMl¨ËQ
¶KKÛ¸õ;YN¸·&Æ®Ürí×óÎó/MrÓl2¯4L¹ÍD·ª%Ò²äT4¦Ì¸#-*Î*¦¤Ø£N.¬4ÀÑMR*rÕN/6ô=¦C*
la û"°n1]±ë0I§@Ã=Ô[è-71c\>26VØÇcô"¨Qf¦¤Áa"±`æB`£&*#ãT°«ZÁ®°¥[I·eÖXÝ#¦Aé(ÂdA"#0DÈ%[\4Bd¦ÈJÐÂTBÑi©bÈ¥`ĺ¬(Ø#¸À¶V#.h´¤#-ÑÍi±¦ÅÆ31ÛXÃ7ëËÃoeÆ ÊLa_#-haqÈÆÕª(M&hÝÌ3×Ký#-Ú´þ×Zz½ÍU
=Dx#-7&ÈxE×èÉØJÓÃ2×Æc<|f0϶ªÃ.}ÁñÌ<D*ôÑ´Ö%¤oÃI/Ö;`E¶dTûMl¦FRT+_+%í0^T:ϳoȵõ¬Ù#%bCB6ó~T(3©]áèqK{êX¡wp[@Ñ-\$¡¦J²ª0¥b\¶ôQ´D!I,D´^¨ ««¡Ç¦åý¨a§,BvÅ6`Ê°·)Ã,P'è·®±ðø8á£ÑÛÕZC\öê¢J¡k#úïHá¦uµA:;p,eZJF7丵mÁ¤Bì²P "`fàS PÂ#-R¥zµ×*õ»òl»Id²#c4ºQ %ÐÃül)ÛÃW =¨H¿Gú~ò(êïçÂ/¾n¾¶Þá¬F¬8Gâ1aäÔöÈlü$há4Fx}o6ÌëÚ¶ú÷£]ÅP«dþ41"$óäGY6Q ¶&H>~ß/,zÒ¹ìÕ~ÍÞÌÓâMnÉmßoXàë!©
qìá%ªáðt|¬>j%å(ºR¹©¦ªx2'%Ó¶²}«c_¹n¶--áAò$B{ËÎFíçEvÏ;pÂll;Go:zÞ0ó5Ï-XM, ȳì àw3NH¤Ê¦yÓ ^̽´ö×c¡ÇEÊ
vͳ2Gè2Á¡0£øR?tÉ¿CÄJäõ¸ôF'5ò]Y£ÕñÄB¼¼¬ØÍY
¼6ì{êãÙëÁbª°)¢@²~\×£®f ϧp¸\Þ±?ïöæJ#-jjKïKñ³T¶90ÐrJe:þD'È> >?NÊÏÐÌzÐÈ#-â)"²B#%AØ>Õ a(
¤º#%}d[(@ù©lÂ:KíáaK¸Ò£Rî[d-¦ ¶fàsÒUÞ:KQ·RZíÒ®íÈÌÝ×¢º¹¥XÚbÊBÆÍç#.Õh&Ùl\Úç´Z ¡X4´BÖZH#.Ú 8A,#.Ãà28dEaòkÝÄ44ÝHÔ<ÆZ©Ms=ØN¸C¤
ËI& KÚ¶Z]®ÍyæÖ~*"bCPÕ"p(#.ÐR#-O3®VkÛWhÞºÛ^«E
>/=ðhÅaHÔ#%eBeFÚ-x·S6ÄþªQk̦b´F-2£hت*Ùͳ)$
³F©4#.-Y©Tì¸ÚìãÝÂÂÃ#$ëùb#%.IH*1TD r}8ýOìöcÕîýõ>eÛäíÜ^zg¦ÌeõöA§)¦Öj?)³F¶û[_Z®%mx®ýìD¬R]tÝÔ]»IJÚì±[b0H@jHÀD¬ÅP¡H$ ¤Zh§ûä9&ÛÑô3#%|åxTd¡fÁÊ"$²Mäãµb¤5£jéFÎÎ-¶%&(7ßuU7Ä`#.©äy5º&§ -ó>¼%=ÃRÜp~jøÄsʹIÐÖ&H<#%*p4{l¶ÌÅÇ![ÃéeR@cUàÆ=1² 44 .PjÕR=²º 4ÃÉOØeëÅr2>n)§Y·9tÃ2OØ`ùøeÙÄmIw§U?õZmjkC©Â÷±3oÕò8/¼Ì(N8g}´Úê)oªB@^p¯r/¶ÃÌ~¦´X6Ñ"ÕÛաƾϫ6÷éSlû§ëÝØïàV3NÃU³s«/N´m¥ØGí/¨~C®øUþ%è#-Ða×ÉɹSC+j^¥ª¤Ï¬ðy¶'çCèØ6¾Åm¿aY5µ¨« ñ¥øjýÞJ1oßîÖ¬ÁZ¾#-²onê£m´UEW5Zòºóé×»Ü@ËÜ~~¢ã |ðAp@×ãfáóêT¯L`½Y.Å,@aQö,)#%¡0
NÈ@kA¤A£F¡Ç0°v?´_D#%È0*D.¨ÕÅSAIŶÛͱo¬m^wnÀôÉZlc-$bõ¼ÆZ&#-$×mB½ÙX÷¢µ^ Q¤µËY«IFȶâ5jÖ3r D 0¢¢DCLP"¼Ht$@
Ð-Mí¥¤µ\HæXD4.lð`úÜ£$#Äv"Ý?»KÞ¬:°wR]}O¦T#41>Ð8]®t×ÈbË*,\BcM2dÉYgeÄ#-F 8#%!Ä#%½¶!!HD u7L$Ko¢~;ÔÁëwdÒ=Ú¾Å\Ø)G²9Ï¡ÿGgX÷*¸vïq÷AÈ4"}Îä9ný°c¹?|Ó¶}ò4¢gÑüdµeg'ÏZûµèÛì¥Æ?Ëó:n«a2*8ñ³èDÐpÂãÛ~MM1ÉÃôLÌ©¢g
ã!Ã+uï4Fº¯mHï¶L#-Ká °F¨¥©¤¶KèP9&Ôĺ^`Æ*¡¶²Â+aɱãðD@û"ë3s+Ëg{ê}iç÷ywù^Å%Uãí;¬¯MRÄá·«F#%,RJÔ¿#./èpé33JYTQ)¢TT"!t"a°ºç}+Q* Þw%f#%rüúÆ#.»bUH@ P#Ã6·F¼Þmo®ýßÔú ÆËÔa,¶¡§J6ÐÈKULCl-uç!³-XRÕãnYi¶E4lðWJÒmί7´tºÃ»wvéÒñ¯¯sSMååÖ¹Ífh²]mn¶±µyªÓgvµÔji©Úê%´Xµâî2]w[»+®·FNë¶å]Ce¢m±·ij¨#%Cûhx6AK /©=PÓ ´½½<ôY£ívØÎáò sC½OìïBÐc¾g±)º#.ìPº ¸:>µ%:³1/¾;S¸o+é¤>0 #% ±fuçÒûÙø¦õ襽°îôàÚÀzsñDeß½?'sÔ;Àó6]ò#%cY#-µDÌÍ/¥^®U´öÅB¦rPR¨»þåü0Q#Ï×µeEHHRDDA$rÔxBñ}*¡Î¡LÊçU:ë®êl±´fîµLO+**"Y¾uR®¡«$Ã'_¸ l²#%ÝHeTQPU"Ç`(¤d%Ã6ô¬VKf2£^/^WU±ÚºëWjYó^Tö¶k]ÀPj`!Õ=¼8&aB|´¢O߼¸úÊP#%@S(T´(À)±Þ~ÏáªÀn@Ó¤5z+Â#.¥Km-¥kY¦µ+m `Á!ÀÅrÙóKDDÔLÐ~6Ù¶JÊ£dSRdÖ6°D$z<Ç#%ó '6ªóã±Î@Ƥ©*ÌÒ-«ür6¨¦´-ªT½^«ë¶ÖßÀ¶3mLø×9¬v!êà% »LÏìñC#xuèH¬ ëîÕ#%µµõs÷áuÿ6#%~uû¦H@È TýaÞÿ4üçxª9/{0ù3¸p¢#-Î`_¿u#-ä@+!!öãyDÇIA]H=¿_TiÞv#%ZÐÈÚôakáR4
àIJFÆI¬ÁT1'"lR(ܵePi µQÁÕZ`A!Hm(4 Ý)PL( L"ÇÂMÖZ#.6Ä6µ*¹bÑFÞ¶Vµ½%zW0Ï>EÁDæeð¨Åõi=F®ÕîUjqÚ4)V4dá°Þ,×ì#.jFi¡#%ã hÈTJ°°4,^éB£VE@¢5MF%ÍB(©¦Ãd Å@ԤݻrmÑÛÁH:¥vLw>ÿ-W
Ù(YT#-L1@%((#.² CX&½ú»hØÃò"d¨q#I/FÈ2¨@"#.Dª#.©ðpTøÁB ²$#-(¸ÑJ%)!«@V¢¼Îü½ô)<CP÷ó9#%ùÿ(µãÌù±?T½b×%dFÂIQbj8øÊ|Ù¤Ó9kÉ9$#ãR&ÕdlY1À'ß#M3ÁZx8ÞHÌÆäÿS.L£ÆÌ/?¶v?¹*´ÞÚ< Ñk亴ù(uK÷ÄÉm©ä9o?1¼Û»·Ú=CBy"Dé'£`n=5!Tó`OjÐ<¡PYøUTÞsÞ&õI~ø¨!I#%Ê *1 dw
6:k%ór0}Ø:߶âߧª kn£;iÔ<?³gÚη >LBh±¤Ò%?M¨ß±MíQà¡ñCP·æG(Nÿ£©öíörûê/Ï¥|K` £å¯%¦[òúÙ ±[ìä-cN4J¡éwÕA75ûËÕ2xRBÆñ¿#.DüQÈ\òï¨y¤èáØ©7&DoÎÅ-õBfEhQkLÚ7->4_4>þñWq¿³{sýãJ·ë@ø.oý\L¬+ùðáÞgG¾©ü{]4¦~6ïJúEøǺùÍÁ¼åUQëøÈ¢ãø%ìy§ ýú?Ãæ¾ñÏZØ©Hðо@ÝOÝÖ`Î@` ÀdksAc.îZ4Ö@Kb±T±/#-áÅðqa0óMU°á1)yÉ¥¤Åç¾*´gbaPõqçÑ×7Oë¡C0X·©\ÎÒÁ}` åk,ß'f)2>4ìíß}p¶Ø)Êjäj¦¯>GÄvwêHÏÙ2_~lc.£I¬:<¶XÁÈYö¶PÂ&,lvh8sO'â{ÏOY,ÜÑëѲq¯>#-ݱªXã¦HðQ b*%)d¹l»sÂÎ`iéØùò>³à8yhã[³Õ«ÇíuÏÖõy¤»é<}BSßÙÕÕ»-ÀJòÏD"½\³Ò`8³µýqÐù©Ö½i áO "¥öÐ!GðiL×ÌâÏbaOQN ïù^7Dr9Ú[øð?¸§ÂßÉÉ#.!½)Ô¥B²ìÂZ[*³AÏwPðÝfÄ#-£#Öõ©ýU¨?È×ÇÓZ¨MQQËCZ_]i©HI®|~l?(ÐpÂHØ¿#¬8VäU¯&¹r¬;¶·6ïç¯T**#Í÷]OB°:uÑ55IEÑ5_4àåerDßõaÏÓ@ÞÌ«cצü¦wÏmØ÷$d;ï#-&MènmÌñy!#-ÁÇ)Ìlç«l%Y1^hEWDJÒhZ#.
îëDàvÄm[æ#¥ßn1C'á6 ²À-¨®ü½)ëe£§ã¬¸bc¢fo(ôÇZÝ$Å#-¹Æñ@¼¹U-c÷3f;Q#-¦hÆËç$=Sé°¢ªîzºVÀ{÷Bo^zÒÞH¶ì#Â;#.ç¯Ø3÷Ö[~?Ây-.½IÀE¡w±,L&´ECÉOØøÐüpù àw¸H2RPîÜ^®(m÷ù®pY÷Óç|éܽ\ ÄæÓëÇÔ{S²]þãçé8;xn#.1AÚiÈ`Þ6¶°¤!y¬ÛFìB3%#.s]8I)SÑ{5aa:AGëimÅ{¼j(³¬8ÀÈdÂ'¹ NÐý÷¡ð¶â[o:Ó0i±ÎV30ع×júiÙSÂ3Àôm6-Sz|ãôZLyL(;ò(¶øUgËPPaDï°!HÄv22BÅùs8îå"ãÈ(rÏ|þ4B7+T92}5V_·ù>µ:3Þ5êúE1
:DRÑ#.'¢%M%àät'¿vß0d"B
T0U°¦©ÄåCß@`ëëÚ0ôþÀ×SµÕ#.ëú¤°m¹ïüóáM³3ñ:fvÅÍMà<ðhzq| Ü_ΦÁ®üÔÝÝ#.Ä-Ø¢_>à;_êÎÅn»Â(Gööò?ãÏ#.3V
s&?¼Ñ£ Í'T#%ñØeÈ%(´¢BGÓnCpäVÞ>ü=>öÒ~çO÷³!OõʤU¦Ün(ùdGÏWXÇr}²qëÙ#-\'#-yù÷bP ¥lP³¡IDü<ÑÐ÷&. îñºØawá`}Käq¢[&Hbega{®7EANmXpSÝP_£.eÐϬ,¨qPßZ çêÓPpH©,P£k/åKlkµfÚSJ" 2=ü¸\9N#%Ŭ[(îæy Ö@;°Ìp9éûn¯¦ZH4)(E6RmlÉ%6l 5)SHR¿ookë¾Zý¾_|¡ êMnttÎZ0¶±©M¦ÎTs×<EÎ#.1®uÁÂ@#MwUÊ#-;ú#-U½Õ¦WKïÊWyIê9S@OÖYö8r69 G%q,,DÏ|ä?Yë2´<ÙÅØÒŪˡ[¡D,ÒÅÔ:¬!a4C°`óÁÌn4`Áq.-Ýç´è$$zxFyÄØ·&Æû(¬½C#%:2Âqu¼)è9y¦ÿyññæâÒðµÉ#Df}vµû3týozZÂ.paûr!×ÏõçnìëPÄYli$ýÑY-VÚ*º\VæºuscWªà*°HRgL#ÚA×U%·g%Í×[²]íuÞJÉ%F%íP(3FBËTÊ!$KÂhªÚ"57\¡B\ìSúÔ#¹iShF¯éÄUb¦mLÛÅ\\éÍ:ª-õ%CÆøCZÂÜÄf¶bH#Z ¬\´¢OupÍ1úZܵxUdÃá7#.ß´ÈB>ΰëEtT Ö»xüEà¨é#.Ù#-f
8¡¢ ͲSÚÐ6Íiók±Á²¢Òm4mÆÕë5SÖU˲]tîymÝ,ÇnÛÆ«ï@·^Û4)jÐÓQ6Aì$+#%è&6°pdKdj)"EQÀQc-¨¢TA2#-LP@c)²¨\*:Xó É7#Á$!³q±`TÊ4ä@ª²°ªãiV6Cp#-½e¥©v-Je ÂQCrÝUÊcjµcsfÇ©vïWyµLÛÌXÅAâmf0Lbó+3è9Sg©²X¶í2
\µ\`îJÞ#-j§Âå#Ã!KÀF@zÄ^V樤SBCwÏÛnÙ*K"`0ÎG#7R Æ5¶L¬éTi@jêîÖ±²Ø²°q3vHSÙ×Z³p-öÍkÁ¸sp9t;¨Àê#kI+À¤f#-ò2"Ôi8Õ#%b¢Ý#.hc[-Q@Ñ#-±6.é|KÀÉ(Î#-3ëú³AÏ"ÙÝ
¢"ù#-é#±°Ê!È2êÈe²ÜÁf¡,@#ÒiF
ÆÁØ(%:!hÉÖ#ò+j1äÌÕg #^6Ù*Á&ÆiA4A¯U^m(ÑîC824ö»DTMôÀ\¹U²²,f½)540 ÑÐCä*5M0)ªclWlµ¸Öé:fÙçcÓHç°æÓEÎQÖûaìbÊØWW"#.M|ôiØ:ÛlôCm+Km¥£± Ü7Í SKFîó1Ô5!"¨2iPîïвl ®M, hQRâ# H\cpa@1-ÀcB(i*TPE"¥Á4ten¥`m)£b}Ò¼y'L©P?4*¾ª%A\Ç*¬J#-±È~§IPcµ.¹zkÏó¯Ãh#-j6Ö4-Mi%µóµuk綣ñ'³¤åÔ?Jæ¼sU¸9:Çòo¿H2";JvdÀ$@u>¯å3Ê÷¹M@_iÑ<h£aò!#.(Ì7#%Þ>0VUA0ZMAS¹`Új^Ç°>ÂPÑÔ;Êò¦C¼uÓëg ©øê~Q(¸äi±Pa¼@({M£ýÖ*ôíëb#-®®®ÖöÜu)Ò&ª 3Ô¤+jZ«I: ë¯<l[ò`åÎÖrµü¢Yç »Q%ÃsSv[FÃfðÂâiN¹oõ}KAn»ÒŽäRbØ·RàGÚê|î%¥÷`bLï+¶Á(xål»"Agçk«FbùF¼9Ëè@ààá͸Úp:ÄþAþã+¿q&³YԼáà¡XC½2s$¬ýÇb5LF¸ü+«sÛ Öµ5ÐDB"ÁZ¯v~'^שíñfEz¾?[Óà;£yIÀókÞÍßõxì0m}Gh~²S?gp^I)À®;¼ùcDlâ¼\"| 2$+£èN±2ô#.º¢u°á¸_5_Ð#%V\r,¢vô¹]©¶ô#%¨«½¾¶n=Yt;ùÏ ,/F1'8°9m9òõ®¯]{lФýuoÌÂ`4-U;F}Ì3§ÜûÏ×ǬչiT"pbba#%´)#-H»± 5ÈÎ)lM^¿Ý0©9¶MTþAª¦t Ëo¹mïçxÇ]ni=.Qog5¼`d@I&"a´XS$-$¦*,oͼZ²c¥Pr·#- ¬#.Ò#%ÚC`(24¬Ê#üÅ¢è"1S0©2 a#.4 ÜîBâtÎHO}Ä>/=!æ0¶²4b,ÈS(¨VÑRV¯@øð9Rí$bnîÛÙUËðÍ<Miâp+òsDöD(@ù3ûÁÚ@ ªû§íRUN¤L&ùJl¦)Ùôû{ÍôåqkvibFÄ¡l"hN#-ðøå*ðAª!©""¨ªÝVm\#-J×Î×l,)0#-Û'#wD#%ó 2BÑà!DhM'þ5PóªÅç£ÃÏ#%ùà"\¢b¼ð:ø tļTLÁíÉyíì6%_/Îr2z>E ÎéÚ±{¢#%á}¶©j¿f[&ZQh£l¦ÊQb4h¡fÓ,L¤É3XÖmª6¶µSSlm¢©¢"#`ÉâÇëº V&1AjP`91 Èa>¼)pLÆ òXFEÇH,È#-#.ÃXGq$+DIÆ,nd(¥¼%ÚñÚ)ÞgÛ²í<½¶W¶·[¦¦ÑEkÚVÝ)T"×^Eò#% PòþÞ·@õPKФ)"U(Ù?k0D`
C0ÔB¶ q¸(Zn)QîÈw³Ûd;*yº[üþ}U¥Ï-RUêÎF"D#%=¡o«&P
ƶMõ$' aD%Å«49ô8M¶LP0IXJÈÈ7ÐÞG[¯iõ´¬sÂ@ñ£s#.T¸Í·q)!Ú ¢ð¤§s¼wIn¯-.TÂeáì
°£QQZR®XY]¹éïpXæ}Q3iú\g~¬wÙÚ¦·X¡1E!ïýxfå#%lí sä¾pÀî}Î\OôbdÆ£CòéÍ0²ý~0çÓ¶·ã Ùäþ¦¿ÚßÏ×Ó7ú&Ûgzå=93ÏK;h6âu&Èë>pFõÕÜÛ\³¦N4¸Ð8Ä$[K!0".&i:]IÒ[o;uGrot#¾fSý®Ô·@ôQ~¿nxÉ[p'7kp éM;\Á`"°Ñ;PbÒ1´Dyù;F]η!Ù».ɺ%#%èÅÄ*çyoÅk9×TM0T1a¯i9é&[cbÃ7v}o\,33n'b¥:3ë]iðJÞSÎËÂ
Ô¦b;L.þtزÊÇ(fîÜîz3Û@¡ñ&ãFèènÚv¢@×Cò|5ÚÆ0DzÑnr8öýѵWä¾[ BÞ^w'@Tüe¥ørf¨Ô®¹¡0ÑC3Øñ-Ueاm#Àj·oRÀhÃÓÇò»ÆÑFDÁ$}R))X¸È²_
(q¼ÒQ]½ß·ÔÅ/doE[Êðz»løØ,QÜ^#.dwÉ£
ñuØgôÉ®=Ú>µ:¶:%'O§B´iãºM9~ÊUØý®WÔÝrVüÀú#-oG·sÒʧLðÇR1=UIÕÇX¸~ð:ëéFó|(èc¶"¢²Téxëï秫ãOäôF°L¤ó½ËtÕÓZ®]fdã56þUÜ£b5)ÙWû|°ê©Á0cFÈoïRÝ`Ê#.ÉÇ¿Çf1³!áo0@éÇ!ÓJ°ÌáY¿åD-°~Å!áá⪳·;Á ánA-»¡1 ¢]C CæÁ#.Óº|3¬t<)uɹ:ÑhÄq£ÄÓµ
AØ8¾ÿ'lî5&Ý7#¨òḴ#.&6àÒÎk®Æú\IÑ´î£]¦wÆ£<[C@×Ü-ãº=ÍRÉp_±hvÝ°=w¹ÁÇBoÒû`ÓÞÒíØñëÄÙÛÝËsµý׶ò`¼¿.îHñíÖõUúÇTAÞÙvXó¹oÛ#*NÃõM¸Ül9Èþmàgµx!F8^=¢
Ñ73*iëJ®Ño¿¤Ï!®]\µéÅÌÇ×Yöÿ#%6ÊÑä)L»4⧠ÆA\Zu u<ð0צUÔ²,hC#-xÃGÑÆHQG®9#n¹ Üɵ¶ÆÂ(Ì{3X<¬ÏRCc%ÆÝoQgviÈpGU1½#%ÐÔ±ÅIqÄ tb%L+¹Ånæ25î7fk<Ù|ö§õDmd*Èñdý)ÜÃ3g;±!ú®å88Ï,AçÔ¾$Íã§MLï|±)Xøi{%#DÞÔêu©±dDsJ+£ÊáßÍ!S^;IHX°êäBÜó ¤Í##.PoëõÍ. d;C=HÃ@o¸ãÊxvØ=wm5n¼09$å¹fÅðôÃaô×w¨´É á2YIÖà±½ZJNº#.sUj\,H" óÈÕÛsã]ÝÙaiåß©ØR´K<²È¤æ¼gÂ#.øyhH8HK#² =Öm½ðÝYEYñp¨iaÌ#-Ùt9Gn´jXúíXÕ¨L ôÅ#.!±ÕT Z7]U¡
ÏÜ6ËÍ´ªÄ´BY»©Æ1QMf'd {Ù>dlòfÌ|ã>3:®ù[6j6ÌgN*ñPä®1Ю+²!.»æz#Jp>îæ2ýD¼C&tÛÏAëûïÞ#-¦ ÂTýß
Ãag´Ñ:LThn×ä4¦åäxÚö~ÿ-N_%'I#èå_1´ |PxCËâXî·\OVm¶óãÉ¢áÌ-ꦶS½#0ȨyFK\],fÞ(ÀI«RÍÇN%/]ÙËÃùnSÎe¡«göïQ| MC·`
ë5Òq¸cNÑöÝí98Ó4g¢Ì²bò}p:S«lÉW:hèïsÞõ+³mÁëãÏ|uÇ(lקpëÁìaµRêyÏFÉ'dÚÃvÃëbË74D.*m>éè ßô³W{+,ñËôG}uÌ$|mß4µD4¼»'Çc÷÷ó=G§YË˧ãânéÈÙ #-n¾äzÏ@3óÜÝ¢DêYÔ=V{çwÃT#.KB[)<8¢þéZ¦¦i©Ä»UÒIí,¼¹à#J2F ),bhc0g ÁU8îC)#.¯CÐÓehaô#Öíºõ;÷4zr~glÉqàËyÞd¯hfú±ã¢¾¬»h>´ðëȤÏ0ôÁiKX³lfq:ÐÃ;\=ÎË Ó¡¦4:Íö-Á*Dc8í=Jì.ì£÷bc0Àw.Qè"y@1/¼*À*DMâô¥Pa$ÝÊMyî/°Ä,`ù!!£")ßÔFú±¦Çg##-ª>`×éúwú#ðø·É«ZÇðÙ©xß½ ï¿~)æ5¦¢ùâ å̤øÐeÝäeHfõM¥³%®-ëðÚ³tM\@¼:Pðõ{K`äg´,0£Lý@t§JlóÁÔ:£ÓÙ.nøʧH1"±u} ¨+L3Õ©<¸&EÊdi¬[&d´ªD¦4ö*î³·mÞº¼Þ·oMh Ö5Êç6¹Z=.ºx*óVôµ±kóµ¹k2-[zkF¨°"MóI=@Ú+Hù0E¨*2( =³¾ùËƾGú.%#%:CÕ8·L±»mÑ"xi-¦>¦ÇxµÓ\Ýån²´Ûa(¡"H0TJrä[êââÆ[H®»?3_]Ö<rÙ!ÃJPüæ ûì¬ëÇ^TbÁòÜ4Üú:7ý?ÂQ³KdÎS)ô]I£¾cU#.2RDØÆ\<wcP¥/>6¼ H(á"¤5\Lªh\¾\iêáÅm"T'+¶ÁÍQmSd-T`²ÕLz±&0dUCÌWË6κÕ1ÃRWc;ÖúÌζ֣½
h°l´ª(ªÉe¨r6YC»z½jcËG!\¬$-Çxs#ÅÁÐåÚ[¨3fCm®uµ:Ä77»L¶ÔM¢XHXK.åºÕÌàËÔ$uËN³±ó(Rm@l0ÂÂS¶àØj #-±¦.#-5 cí»µjH5\½L8Y®#-\KujãK³[2u*M¬m¥¢&ôù×4)vÔ°»jm¶ôi¦ªhRCCÆ,éVæ©¢=¡¦¶v©¶A¶°#-)TTT*ÃÞ Òi¦Æmæ¢Ì!ËÏKG.äF20f1p2P¬â«I©«hiºHd=i»
lÂZ¶R JÜ#-ÁãZÏúÎ,¹¸,6óq¦"*Úao÷òÁ͹#cáÔÒEã.â'#.ân̺ÓR\âèBÐJQKML*PÊ-5¸XaSR´¨Ä±hسz;æìä¤ðÖVÁÍZáèÍǤQ¤õÈôºèÐëd»,mJ8UUMªFf'øõE V§*¼¹¸¨¾P¬D÷"×{UqNé±S¿x¸8 >¤·´äxÐ{Û¶ôalÊSiÔØBø&Ú0¸»#.ÕR4ÎÎá!²#-¦é&TDIX6EXbaFñD+ÓÆ´´À*b(×MZ·Þ(CØÃZ6!ðɵ »DC,îª0á«ÇØ@!ècíô}pü Á"¬!KØ«£c6¿@Ûàt2S É #M>ryÁô©E*JRIùõBTïxÓ@} ©ÙTò©ÞkYp#-3)iv!È ÂEUTd£$=Þ¢§½%}ÆðlzcôºpU¬ìZ¦/Ûw î'¹ª|@D4úût$î%¢r¯ªròéÐà¾<0¶òÚ·Û×½B2-Q
"jZw÷ùæzöÅJ;]¼ï3xééàz»¦î»¸¨TÃxõki]tk#.)j¥F¬k<3Ѷy½Üg^Ruí=©ïF«äw@Ó¡ÃJr#.8M¬Ëe5PF{®êýGOÛ´zøHyí
@-#%J¡Ò¨¸^¡#x$X@#%@cµ^M·*«Æµ^eEH#%óM_ªa<â½Sò=~2gºT¬¨ÙIh(Ô!BÊL-©YfÔSMjÆÍ)6ÄLY&4²Ø¥)¤Ô4Ò¥" ¥h-d£cMÑf¦°T&Xif¤a#%FFDý
úϽsØð£}mÂÝÊv3ãîÕÕ#-·4»z£ò{Sf©¿¼EÏr9Í7w§¢#%îÞ@÷áv_ÀhI$lüL½fæ3E Î3 @\x÷$HLÌ¿aÌâWqß¿;hÃMÝÎKñ5eïâ{<¼4PfΣuð&»pòC?V~+åÔÑMh| ÎÓoÅæ(2(H "$'(#%ܶEaIÄkIú§bH+@eô%°.{H>44OÉÉóÕaõÒf;mß̸jDÔLÁdÇï7¡ÏèeÑ¢°j&rÊÎb£(èËEóv9I³cxé¤XŹ¨eÙ&# 2ÈC#%Ò8ÅaRB,"«=M*´Jb¨e¢`£ÍDÒcXù¤K_\0hƪ±Õbb¬´ïzJT4QÄÈȬH¤çÃâ~£Z~nY,?s Åx9f11¨ "Ìë"v6#%ØÂTbÄcB¥=VCY¬íè3ÁR4FÜdW×#-z"%ÙÂ7$ºmn ¡µòhm#.¶,ECHÃHö²þª³0!ç˯÷Àí$¶Á)EHÏOY`| yϪ²Þe·è.xdÞÄ7©bsïÓ¡©?c¢Ìî öÊ=2Ú>:+CóeTÑÊ0\5TÓ}H{èPY0Ï¥I.åò#.:ñev½¢0¸EQiéÐ~Ǫôn7RTQn߬üü³å^W®¥HFß<=öùÔÔgú]ø#.i4°u>,л#%ð0³Gc'Ô}Ðt~À°§ÌérWJñÆìc²*¸5Ï`¹Á¬#.¤À(ÃTs²ë^*ó´äihAô¿]X>`áé´µô'Á9ÞQ¿Ãð©"iïÉ£Êz
X{MJMw|½s5¬&"k£^~¹sBFQÚc ßè1GûàL¾sÒ=Û¢@;gªt¢FF^z±5»)5ò<Q_±ºk4#-õPòÒ~ÿ©§ª*¦Q;úÛÕ#%A@X ÆóõRöÕ(´bñü³ÐlM4¸$#%øÊF¬
ý|-¼á#.>toàÍ.âή,1CA$EZ¯`08"äýYMÓº»Ð½ÎèæÜz$wàÌ{ª©÷§º±OàÅÅkxLéã¯èn§»£"m:`pf±Ö«Y¸°mG¶mÞa8Ô£1ú×F6×6þ¹5Á¾îQ&$IK-6T(¨U}¦nÞ6̵ÔÝ.ÃUAÌqB¨Dûà³"d:Ù¶§í¡haý¥*Ù,5éÎç©«(l¾X2.øc vB!#º8!!dB°X´@ÖTfm*¡ ¶Ö¨ÆÁXµ"arà5-{â:ô³r#-A3eÒôcZ÷.ñÜBáÂ_e#.eÑ0ç#%o}.,J#.Ûv¨³D¤èQ¼ð>ã#-Â$\HQ1ÅPÆ·!Ê4í#ÓÅ£µ°H¤-Ø+*5zBbT #-ôsæa×#cLM´ªsס£#-ä#-g%,44!²Ù¶ #-mf³Ui.¶I êåFµ8¯êóò}q²ÈgnÍn¹äD¬4Ò¬UjYìîa-3õÿ½¿EÇ0ÞtrWª·\#%®]º^Ðbñ~Ü9à¦b\SmÚt
!»n]±·°X&éûìJ°FÃ4WI1ò9CÓB²]bKȪJ
£S2¤%\ªu.é+*¢°éðêcÊ^îÃJj8ÞMîÌ®;`Æ1ÉÖòÎX%ïN[ 'éC;¡².6d3Õ¨CNT8ç²+°`Æu»Û0´±*) ft´ÚT!©0DåÎeÑ#.D`Z!uÀiò=ÁNƱ Ú#-#-NˤATL:R¨É9Ðgñë;WÁ®Ý'DÄ>ÁvÚÍÍjÈ£+cNSâkÐ]ØE#-¸údÝ8Õ¸ªMÃ>%²HD¥o#.Ùå]#.¦6«QAF@Û®6¿#.±ëÎJºÑJº%ñ×1²Ïg¬½ Ó%tlä)g(XW»íØr×k+·}ÉÈ@lÓOÜMæ£SIaÜITA³é
M-ÝF3#-ÊKjNl~¤b#.@NÂ\N+R]Xùq/6Es. rϪddÉà>n<°ì±e=Çòk¦¹;KçØé4§À¥FÑ»1!ºlí|ÙÁð%ÚÖY#¤ë",`æ®fÉÏI¥Ù3±ôLsñØ®ÇÄ÷ÀÃ#--V',ÔØ4 (wG¬®acbÙé¦ÐÎÒMV"*2EÁ2pÔçñű£s0
tÌf$1hV è¡"9ôáScy£iæ[̲%ÊÊ9@ckq¦Éàu;}]Û4Ökh ·7a"Rl¬lUWXh"ÉzmnU±j&a´:#%anÐêÁl?+ýÿcL[g)")¬EÄ·åö¼{m¿ÌðÐRB 0uMWÀïæeÚ^¡¾;«HåŦ¶ÈÕwOª®rüj©F6¬¯ø fpìÖ\ ܱ;ØE5¶òͧX´ã äx8?«£KlDibÜJR, qqLÒ|ÄJk·jdÑðt8 ètÍD[oå"ìùL^QO`!Þ÷PgmÕ[ôÑÂÞ̽j"±d(Á4ÑZ.ÝMl×maÙ,ÆÎÒ¡³{£E>¹ eláÛ£¹2;iR±|ÃU»Ì_RB2ëÌt ¨Ã³Ba µÀøó7beB´$p¥Èíhh] MSÂ*º»¸»±ö½ðì(|i4#-l'BÄ ÌnEñd±¹Z:¥û¯#ºqn0fßjh(Õ¤A#.D1uM¼è(ÃIWJºSÂM}tÖêø¡¢v#.×!²7fqÙ¶i<"35^ì4D] ÐÉ-gN&·[% 0 fLMÌî±"ª ÇQªdÉ5ÜäÍ&"«S`aFDT²×VÒ6ê-ymuÅÚmï×Ö©ê v£(ТFÔS¢#-hvd2Ðè(Bäf¡#-ÜZ
Bª¡#.ªs5K1ÚR s`ÄÈÔºîhªPLÒAÈØB㡤DÑÈdH©`C½#.líÔoi#-¦¥9Á#.¢ÜlA)¹X,Án\¸pPàUêOW¯~îϺÊ]*a$Îüæ»Ôv&üüýrzÆÁ=ÜȪì#-QF! ¦Á**/2+öpþßFÛaT<³¢^¼ª¹T-Ù3È1ߣ¸«Ýt_NÚè;mcüåà1rJo)¬§¼égiJdVù%´v
ß\Ü&ÞÝÏXÑ#-õPL8¢tõ7³¾5Ür±E(¡¬ðÛ¨StSª¬µ?!#%åî¢Ò"ôHÊ°TR#.CcÞÎt5и~-Í0v.Au)ÈÊ ¿É5IÀMUª}bfb·ð ³¡T"V(òà·¢;Kã®9'ëkJèGwx)ãKGÖ eB-û ¡51¾Ù.XI{¬±K¦YÓë Úï3dEMÝa3RÒ:ÄSú/}~#.*E}eÈù'AÇäg3µÛÔ=ÐQ,¤ûb.ã!óð×@ð#%û²v*¡ðJi#.l$!OaÝ* ß¼1Ñ0¥cB©bS£}&¯4#-]kÎð»#-#-ãi1±ä! c%Èð ¢A¨4Ê #%Ȩ´ FÁûª=ÐõM»!ºSÓJ3Þox[Í%¡0ÑÀ,ÙI0aÂ>]¡ÙÚÎïéß®ÍÀÒ ª#.h{6|XÆ~s5+#-?tJª8Ä©H¥¹ÆZ*Ð]@å2¼÷ÅòØTͯ&eº¹ AÀ =!6ÞwuÒ0â¨6÷OD^g´¨«G\scQqçü¼r´Ç|4zûÊÉ(>ÁaàziáåÅa¿a tôX¶i'¯)Lªå³#%¬bÎ×èg¼
7]7?)©Ø©ÙÇlT#.B[£; bD©H£ÙÍS
ïóJO³ãX.e»2º^È{áj1¶Kâp]®4rn5¹Ù÷ï£|ôT_\@=°FÈ!T e¶Rß©KdµIµ'»kÈÀÒ( h@A7¥Å. !É$H¸ïõP¬±#-´!ÙF3'ùòQ_xª¶vO~âutQU(Ó¶-,É¢#TPkáçÃíü~¨3ßLãÒc1OÌ)¬,$ÀÍ_»G¿vNÍ+|FkY¸éb0»FñµÍhoÔ¤4!O80Üß*Mª¤B+.@°v¡§gtªë^6®h«¤¹µÕQ#0J!,Ъ2(.]5,5Ú H4:mµc#%A#%¤#-·2,Å4¬_r{:;G :ܨ¢0¯×BóýÇÞã½à©ÙØtB·ÂË¥i. IÃbýÙÒÙ;MWiLÊ'W]ö]ð
hv!ø8¼¿8ò÷½m|ìæÛdým4ÄÌi"]aXh/\<:C² 3³\¢#%L±Ê@ÑfLâ;¾9×*Tpº#-ʱÂZxsÁKNAxµ§³-¯Q¶è9êÚ6ÝÈG$¸åãfvG»³ÄµC¯ãI¢ù¡_[©jY#-½µn¦OaPØkÚXÞ !AÍ4Djªï&»É{¢eø4|séO ó¨'kïÞzTÈ<-"F ,ÔÊS-#-IC-mdf¡#-6¥ûMÖé®MZã:nå®ÔÚá¯#.ÖððÄm©®f`/8hKyyiÀr¶ÚÚ#%x 8õëë\üâAÃÇwNËd º "(ø²dtÁïçÖ[|z´À`{½ØD#%ê#%9»ú÷§çíéÝO÷î5#.Æg»çY¦m3#.uF ñ\}{±aF-?!ø(ÂmÈÞ¦ÌNè"i¢1X=Hñ&Qd;î%Ã
ÄÓJô£¯êÐJmÊúk
[¸;q®L-;>M#%Ár v³µÌ8}Ì4Û¤NÂq3ï¦ÙQ®åBÈ,KMK#.oMH×Ó¼ Ðñöâ0ûU#%+@&ºMB¨3T¦jT0ÛÖiíµ£Àe¾ê¯Ø&îJJRS,ü,#-¢j9J¤2:zú¤¥G¬ëmÚºú#%¤È[â TüÀ+z3åÓñoí!ãÑ×Å3Ös:iìN#%ø @T0ª¢DWq×®XOð£Dù¼åµÖr:x¶U JcTá¢D¯O(vîÁ#[¢ðöxl=õÅ8V)RʹÂéó¢p.`fLuÅ¿´ý¥áXvöù¶[Ó*¥H ÎyU.¿q?¿¬ïáø"æö¥/Vö¸e²gâïPkL;0¿®tÞ%½ÇöÒѹÍ|ûJ¨xzTøÀÚEî Nÿ×`»`ÙË絶WÜÆÅy×1#X¶C" îåõyÍå-
a1#.ò
Ë@h#-4Ä-^µÛ°
¼£%£j)ÆÌ©¶µ®ÉµûÚ»k©EAí?BE6TL¬¶ü¾ct<|ÒAAöÃæ0aøXxj#ÐkôÜ/Öñó3ÌYA@BDR$#¦&ׯÏ}µµ{þ(Ú%J$,X,Ó%³I-_ø±±}u_Ü¥"ÆÊcI¨ÚRZ×äþ^ï!½é¯Ümªä&dµ×í Ü!p|ä[o;ʯ»2ázÕ×:ÈTf¬ÛªëÎòаe#%^P5ÌA*ÿNEÑw7/7ä$ÕÌIoàbºÚöøéôXffID6¢fË°´B2ö"Ä@Ç9N@¦yÌ)"z0>ûÑ_Ãe£@£M¶.î?£ÇTøL5D`7'ïÖ:"å ŲunÁÛYÆ·Ò)Àäz#.d(t'h#%(U#.4MÔݳZ®%U*Ѳ*¡%P @ÁÀ¾\ú{8f8Hª&ÏñWE
`6.%%SG, ò¢PCzÏ?¥OKÖ$í¿K¿QIrÓm1àìèжufå¥#bì°TÍAÙ6Ôñ%!7fé¶3Ù-Lþ,xx5ÏóÔ§#.6a~Ã`\,~#.úoÑýõo÷6Úº¿=¯Íó:0YXV)£F¢cV2mQöµ¿WaEm*~k?%#-BÂÅøÀXÂTLÑåE4M9xì±*
ÃvÑO9ß>áwm
£¾î@ýÀBAP,! !jR°&ÔÙF¶5 ñ6¾]¹jÎÔ*©D¦<~³3ë-#%_cÛ_B2Fð*[ £®ôsIªpøQUx»²î
ÂjÉxÅ`»bÉB°y¡ÉÁ%%#%ãýL0)7DH»JS¶×ÏΪÞ56bfV·ë=5u½8§ó«!Á!l°´A8yÔ2N*b\ÛøMi6Æm3h8Y Oß¡Aõ£7êRPP®%Li¾pCtÔ©Ë´±XuIDdP,@¸QïÉ®RÑ»»¥zkÍæÝi-Ù©]ζTÛ(më»\æ@@`(, uRX°º=U箯O<»²æ6[e6¥4ËfZé·nêÜår#.!9Ê!jê¡ÄðC¼Ó{
ÂÑ]ÌnÏöaWf2ýÏ\ÊPØ£¸.0CÇ$G` Û¿pí;¸#-öÅÚEôø~cåùO=#-Rqt/èô½YÚ.0Sc4Z1
>ʬ3íA#.)#-/M:p0G÷\ 8ê©G8Ȥôò)80z³$©$¾Â6óÎßÊ%Õ+0O3-¡>ɤÄârAàP¶PÒvA׳n¶UW¶#-ÞÍr|`-JTp½&M¤^¨j%YÅqÐYfa=òìÄH<&õk¹·¢Z£ñd\+E³5áÁpÏ#%äJôÌ#-bv ª«¡°5Ô(ùoÕ2æ2ç6y§Àfâé²ØH0½+ñ-.EÉbQ$2 H\ûy;= òþLQzÊÁ9ð BsKÛ@þÎxÀ¡«÷lå!H©3´1¹°÷H½áGÈè7þê¤#S4{ÿ<ÎÉ@gÙ["YR4?²[î4^1FÛ±~¨îîWÒgF8T'Le·3XñPnÙµmÑߪª&A¦l,¥Sö#µÛd5ù°l0ÏÀ$P5LÀØY3Ë¡:O±ÆW#3§Ï¥1»Ç,xCÈ¿jce Ó~HAÎäH¨áÝ©¦&¹¨gb,§á§¿®#-FéÔ?]#.OÙ²a@ü3öä7M%jS,P»Ð:òdéúÅÕÞb^ª*´«ënwËx*³».í=WÓMpªÐ«@Sw«f±1ùâ.ø iîÿçÿOÇý?ýû¿×ÿ_ù?üÿ¿ýßþ¾_ó[þßþß÷qÿgýéÓþ?7ûþ'ûþßßÿü¾[:¿úÿôþöôÿ³ýßÓþÏú¾ï×þïúÿáÿýÿ¯ýö·þï£ýßÿ¿Ûÿ'ü¿÷ÿ¿ÿÿø£ÿ§ü=¿Ó?íþ¯úÿ?§ú¯ü5@ÿOî6ô¼ýÙ¨i4?´!² ¢jÕÃûÓ3ý4 ½ Y4ýuýÏüH¨MÌHBDMÅêèé{ÿGÏÝ2d&S/®×á½´Ðè[XM!ÎôU Æ;h¡ÝqârB¦¢#%1|ïßù¿!ïú4ÜÞ&|CZâÔþBY¡fèëƤäi¤5è#-ËÖÓs)ÜNàc?Öp¢xMy~GKþiÿVcìîðð/õâiQ`öLROÝ¡/ ñÅ èi$$G;{°þ¶j#-À:^Ý}ùè×<5ÆaZcPÆ£:f¥ÅØ÷Y\Òmz´±¶5MiY+¼ÌLaÏãÁB,â]®Æ1ê$U»úÌîà´Ê±i'Ð{Zd2ãT¦Y»-ÑÄðåâ¢0³NkFõ´ÀÓ:öÇ&¶·oF1n¬×¶]#-ð×kæIç©¥ÐÆÆVB £ÔtÌå7ûĸÄô~\±ì½³.ÊbW)ÑÇÂfæ5b#-d=ÖåtM´aÇÆV:·
ÔFIkØw3¬|2c#%@öÃò&õoø`¾ª@|Ѳõí׶63©y²ý¹q䨹#¿la(Õql(À&óúz½{#-¸bf²¢®ÉµÈ?N«N+SñÒ`x±3Dc¢C#-UH,§¶qÝqNÝÛºFÈHÈ7;åÃ$â;V*#%÷«")é[¢ÍÉ@$!1Ô2âï¡ßå,æ$Kz>Ϥı34ÿ༬nü}»6æÀ39l¤GvZ'"$"¤"ÓBT,F&JÖKRmI%¨Å7Æä#.#%ST±#%K¾àösYýä`î¢Ë*Ó±ç(PA0Å=ÈÈ£p)X#.ߨ|ÇʺaÿF2H'è'¤ì£cÍ#à?bºsYùøFx~éÛ#-z¶÷eL3ÆtÛ®ãD±9üõëÇÁ JÍÐkVåoeÝ]½k5ì#-pl!PaÛx/'öÏçv÷ìÈ/¼ªéE!e`ofL¤ÄaÓuÅ)R|¨°hZÚnj.ùsg+|a·÷øì{(Öl¶^6+]«*"F8Î)¾)ËÏ:Æ¢®ZôÖñjXlbÒµã}mâÔ6ŨÄHX, HNÝÓ&7Ó-`ÜMßò/ÐÐý4à\x@hvw3wU¡³`BR+}ÿïAGTxJ®|Ù*s#.`ÃåÿÈýWæ4CuB@G̺ßù^ã¼oFôs}|äBëÿp0#.à!Å>F£ÿ<$@`DSkµÇ\Ô!!B© mH×#-®Úºjù?¡²¯o#%Ê @>ß_¿#ÃèÿïÒv¡¯(SÃ|£ÝêeÄÃm¶´ü#Áëº%à:Ê%gÌRx}þ¥Q ;>Ø»&EÌÏ<T$CÿÇJÕê4þÞ¹ËÿQUèüÿûs$%?a-#%<?á#=çþ-·òVé-ðsÿaYÿËÊÿýt5zêõýËW6¦«¯.(èZëåõÒG¦'Ä>tèP-õû=<Mé|Ô·Îñ´óóÿ÷*oÚáìMZ(@
ò¦kÓi!ϱ3$ÝÚ¦SÿåéS9:3\xÖÛµËÚQ·Äº Î5Úÿê¡m6¨@·ùóÿ3&¦£¥vñ©|µ\påwÀ8!ìùè»l@Î&2²S?Â7¬µg~1FÛbµÜñòç`EalºAóCÚÑÐ}07õÀ>Û©)Öd7#.O«¯üÎHjocº:{ö¤1Ïùó¶oÚ~S#wêýj*ÿrE8PZ"ñ
+#BZh91AY&SYÉLt"ïÿÿÿ¼@ÿÿÿÿÿÿÿÿÿÿÿ¢¦x#)!2Xaé\÷x¬#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)/ºúÕ=[Ý{{Òá¯Z»må˳]9yo3ÏZÝ»½õ÷SÒû5Km»¶÷×£ß4àÖLÕsª_^/^Ü&.ëm}+Üe#):z{·¡íªì÷ºìUg:vÚi>{××ÍÞçÁï>jWlúûí÷Æ»·}Ýw½öpôYÛÆÞÚîÇ>ï¯m¼Øv,¶uÈ#)#)#.#)#)yì#)Él#È(MÛÞÂÀYÆ`Ë'F]Ûg¹î¡ ¶\ê¶#.Bí©¡I#)×Cvf(ªR]íÒOcATQZÉI#.7 mËmªéíÜ<z÷½[vÎìê¬d¶hÒMyU,j¦zS/ݽ{wß};}ÛºÜ{|û^ÖY}k¼íó7Zö³Nì»[Vónßb¾÷s¯WÛ¶hä#)R¦Æ¨#/¢{$QÜ»¯3Æ#)¡{w³%¶dk{Û½Ûp $UPÞg»][i½º'¹ÚíÎÞðÉÞ·[½n÷ÝæÛ·ÎßYUH:lVÖíÔ×mòÝom;gps³:ÉßõOkl®õãxß{Ü÷ÚµÞÞõXZcÊÜצ¼J@#/U&ûÞ<5õöó½=P®Û½Ão³¾òv}{Öw\ç{ÛÞíu=鯽ï5>÷w³×¾ÖiÛ{ºgº»Þì}ëÜoJíâ®yíÅv;·ëÛ6÷¼jöª´¥¼u¶Îí¾ªBn7nï}Ís³ïï]{çÝÙïI^ÞÎ>ÙãåÛ4¸·Ý£]´wvÙ]Ëî6½ç]½wÓ0zOó{ÌöoÛtÜÓP¡P Øwf²i»r¶#/¹u´çFæù=öò<ºUó½çÛÒ¥8¬Á¥¶ÆJ¾,¶Þ|sß4PSß\èT#)ÞÍîå}ÝÞw¾>ûÛ\àxï¾îñ{D½Ø[®ºÓïmµowbn£#/6Ï^»µÙɫɽ¾ïuWÑ_{®ó>öñ2}ëáôÕëï®ïW¯Q×»Ð=öo©_}»°zµ»j[}híîç½÷}öíÝ.·ºÜ÷[Çw®u·´ßl6µçw9á{[NOrÙµäﶪÒ6önæPO¬Ó,ë}x>& ÓCê¼5[´ØÕ÷ló{ÞÁð¢@iñ#.:ù¦}s{¸>ÊvçNÞösÓÅ[q«ßmn÷w½ÙõõióÎ7½'ßX}÷»A·\¥Ë)¥¢ûío}bù÷Tõ½Å)õ¡î¶Èè)¥³%Ú³Þó»°6Nîw¬i¼û[ï¼à2BÍT+g!÷°ãåÓ·¬vîw4=.s¥soºYÝÖÝé½´(ªH½vâv·*Ù÷ìçÓݬ«¯s{¬®ûÙïww¯¾õÞ;_ëßN¥_z·fçÞÚ÷n©N;ÞÝäS½ÓªÕé^¾Þ2Ýv{ÞíÉ÷ûìKQm\¯#½zÝäÝêø´%4B#) #)#) É£@#)&L&¦©£O(PÓMSÐÔi)'¦5£M©é=!§¨hz¡ê#)#)#)#)#) #)"b2#jTü§µSÍÒêizFÔòS ¨#)È#)#)Iê ªj~Tª=¥6§¦¡§©£Ò#)#)#)#)#)=@#)#)#)#) ¦M#)M`M&OLQ²iP#)#)#)&¢ #@&4 éSõOh¦ÔeêÕz¦ÒP#)#)#)Ð#)#)û|Ûú¶ÛWtI?³wV5sZÍßom©»%£á¶Ôå)) Z$¢#){V@åöüòÎX?æì¿eB?8ý0LØã©.$
5dÆÅ3doç÷Aÿ´Rù¤ :]XA#)»/¾_ÝsE¾M̺Ôq·ª¥Ò8æÈó+¹I0zË|-8Þ]YÔÁt>±q/eBJµJÄâqQa³[à;Æöu8@!ÿÄð¿äÈx±!4
(ÒDa*¨¥"CJÌJ@Ä"*® ÞI#)iD(ÞE(#¢UHû! °¤ª8¤#/AÔ )æUÛ%P`ÌJC3LHÈ
Í£fh)©±2RÅ-¤¨je,P4$&©Q´DJ¢l¡lJÌ L)A±¤hÅI)iª$%5$`Ê"F¶T,liblPRJ)¤HÄÓ6-SU¦ª¥$ÖX$j6¥«³4¦©,fª%M±1³TTI5ÌHÍhÔlf(Ñd´Z)J¤lb`
LM6(`)°L f*d%²@KÉdI)%%6)2AQ)Iµ j1¨l,L(¢%iDÆ,LTlM¢e¤4²4T¢f¤*iM$Â@¦ÄjK#.Dl&**"#/ɦl`MIE$dBDÌÍ I31³)¡,b$"HJa I4ÒÐbL,¥0lÚ#M"¢¤J3A¤IRÍ,¥K$Ù¦Q 5,RY±£+$ÌI"À3c,¨È*#/Y1´YCd£24IKZlÊc$jL
2RÍFÊL ¢QdA%() FHi¤H45¤Z¬34&aJBI²E²E£I2I(J,4`",E#.6k1RXÐJl2¥1 #.
¦MRÆ,ÔؤÊ,ÒbJÙ$
TbSLh Å&M)&a²l2d*#)k$ÊÄÃh,%!K&Qi ÉL¤eÁi)TÙ¬mLdJHѱ¤¬LhÂHaQ¤2@KÑ´jR1EbKI¢ ,!Y#)Z4eFi#.U-m ¤ª(É© k%ÊHÚ)&1¬4HÓLÓM¢ÔÈÆͱ(Щ²ÊFh¡K_ѲVÈK(ÉMbÖÅ¢£BfTDÓlTÆ¥ªb(lX4¤IÌld1¢¡¡5EMdÈleE¡bdÌjZY¨°ÌÊj³FY©¥°Ö4ÙDTe*±MZM¤+6V³RRÃm1Bb´i©Q%¦QDe¢±¶f²UfjV*J6ØÚ6%CDB¨¤*#.±ª5ÔÖ!5E´Ñ¥QH&Ã5(ÚF£dhƤ,jLÚµV±aÆË"eMdIaI3j¥¨¦ÌÔÒ¦kJ¥dJÛJJM©¦ÔÙlR#.lÖfd²ÅÁ©-¥´²ZFÄ@`i±¤£HQ°D1mÉ0)µRD¢Å2ÆÒMh4Â
eDX¤cBi¢)ÊlÐÆ CbÅ!R³RXmdË,Ì!#/RÉ ±QFÆÄdh42S1³¤¬iÍFMlËP)¢±DE")´d,Í¢Å4"¢4H¦`´)e ÂHJFS`ÉlBÒ$f¥¨Ò©#.Òl¤Ê,ÑV(Ì(I$ÙI4%£)3H5lme6V*16)4Ô,É¢¦[R2e-%%²$K1 Ó(ÑD#.ed¥LDÔh¬FØMEF6ÄVL2LÑ%&h¦#)6)"±`Û-4T£Y3#.%,Èl
hѰѨFË#/ÐDEd¶*c()5JQ5cXÊfhB¢¤i
IDÑY¶©2j,Q+%¡¥4±¥%4IRÉ*JR£VÄ¥"%+e%²i*,m¶f)l¦²¨i&lI±aB"ÅLÉC4`ÚLL±"Õ(0Kf"j6¢ØªHB¡c@FÊj£iX©#)ÐbL"MZPQiMRIQ[ÆÖf+%¬È#/ÑlÅ(#4cfI 4`ͱhØ¥6ÚJh±É¨¨6Ë-FÑX±kI¶¥³d4²±*Z)*¦¤Ò²£E(LYR¢ÒVM¤Û%dÕM¥3(²mEIFF¶Û54f¡²X*eEF° Z#.&¥³VLS*ÑDUѶŢÛhÚ¡MkjDÙH6cÊÂÊQdTEÌlII"!jMb̵L|>ó~i¯éyÛÇð{ßÍ\«E½fý;i6N£#ý¿òþÞS´W d]j¢$LLY¢ÒEpÿ«ýT|#.¥)°T\AÈúgQlÿ©ö+Û=i÷é8ÈnT-²Q¥U¥uI¦éDn#/·ÿ5_Ͼ!kÒîÿFÃû>û®ÃüëI2hÌäDL³¶»Yb"< #.¶nà]uø^Ì?v§;q0¶ã?¨
NéÇX;£QLC§6íéTf¥5Fª2Ù$¤©·_Bðjck´ðb¡Ø£Â¢5 UwCkJÉTe0ÀÙ¤J5äØ2¨
*(Ä´ÊÍh¥P5W³Ñü½Jô·³ÍÍFÉk1Q$âQb-ÀFiw.u@(þó«ÝçlËÙ¹I¼ëÅ4â" ÿïCýª0+1j¢AÄ6.ZÑ1íÃxÑ#.-WÇýº4=þÂv#/
µÚ*6P
²P§t+üsãLãPºÑí}[ï ÿi<;ð:N¤¥!V&CSMõlJ=i¦äâ+kqeÿ7ñgH\Zp»PD(aB1¼Üó®ît]Ææå&¤ÊDUCBWÂyChJ;sÑ§Ó ¡ì(*H#(k=?#/\nÔØ28¸o}up#§²®ó@ñkñÛ^éé{®¥·³PhmØ£Ö21ðùÞÞHê4|:EZ9`ÜZ©R²ä 7n\w]uÓEA½ê·w÷/«H(¡ä%³[5¸NDÅ~uKꢱü+LySÿ5éeÿB©6J®YÆ*%¡x¨ÈI+ù1ϳ iuÔÎ%É
VQøUkÛrú#/ç÷EÏèñéìâ£Ær2?f_C<á²B8ãzçÿR¹5NÆ¡ßÆdÏ>5~ıqC¡.çíí}ËPÎo¢÷Ìå!³Pö`rÆw¸C:¥ÓÖèÛAÙÙW0´d;Ò×¢ÆÑX»0¬MÖ/RPX%ç¥ãèü1F*'Mh³f ØÒÆçµS+áîß¿ZÄ#.KHíÿ!¾ëØc$4Ù&27åúí221ÀÍì¢aÍ×e/)Qì´½ÅFv¤8ÆäÈÈY[i¯ÙÒcB'ë!î.êm+Ç3,Ås]He ¡»¬ªJ]§<0=¨ÿ³wîÄê71)¬$LÀÅ¢Ñ`Æ# Rzrå}_?·ª¶±¡[ByÖ '
-%$"K<P4«µËcIÌ2ø3\Ï%²aj#/Î,³$©±6ØÀî5#CÅ TX]Q4£¶ôõæþ¢è),ùPöÍåDå¡DJÚÍéjõ(µïï´ò76êoº
4£ÜîÅq¤Þ¨.´BÒ$¸àPj{)²#¿¦kJlÍFd@îª-¥Q4í»¿,ªJgoeYRLHÀ]ë²áͺ$cb}¤e|¦x<C%¨÷b#.²µ¶eL5|HÙæᶺk#/ÓÙÖDVxvT<YQâµôáF$Nú¦ûª_
QÑ}ÎS3»{ÚG½W'²¬äéZ
WµÍòÂ?ÁVS:иí»áW'h#.lÌMZÆÕåPý¤óڦì!aç7?Òú_}ÓGúzô¾m.¨l8ã]Y?ÇÒ¹3L4ÃÉ Xm½§"úr±5¸uìFÀ*Ð!¡ÈiZ¬mØ\¤<ad¶AëÙÙY.ÞðÁx#/åÄÈq0ÝM6á¯GÊCay¯õ'h L~²XG=ñö«>^3ßa·ÊÌT;ïLq»óÆV1^75&}U4B?->Ó36T ¿sª#!slÏdª¤?¦Bü<ªô©«(uÉ=Ròx6£WÍüIFY¨è_ÚnõÐh¸ùyϳwkÇ'~jKOWáÇY/(yå
ÂHÎl4cxf|%£>ïÎìË[mâg
ª¦nìeÕDî©H/ÃÝVûÚ¾6Ý$µEX¹ñycüõÎ`æÕ.9=}T
Ì¡LÆR
Ö¿
ú´£GÐp\¥dFz}yp2f({¥Oì8kÏ|zm¼!ý¸iÒ#.4ÞþüY4P¹ãs]#/<oµÁ*>ík¯0ãU¶&#/jb<}L¹yò©¨È ´ã\\¢;mãÛ3FSj´Bþÿ×ý92±|pÆ8¡Kj¹æËõwÅù´IGAAH¬ÂäûÔëPsçFÔú?Ó¡ÑÙÑc Óó:ß(õ&#.êë&s!檨MFT¹¥M϶*¢u!;®¡Â>xÃIj²<n+t6=Ýè± 0aZho/3S%»Îø¶PýÓ0ðJmb¢iSvÁwnÄ4ønîþû}¯HÕE×évCf8&gKUßtÍúãdÔ¤æHÄFÜñy½:#ÌËâÝë»by±Á
"nÞ4Aoå.^&z´Ñi¦4?eÿ)³üPÇP(uÅ#.ÄÑòµXÜöTWX¯«_2ÏÉÿ)có{ !Zè\G7®pckçká³+|ÓgÞ«$lõ#.Ù¸¸jÉÄ<Ókªbð{5÷E|¡¶:ñÖðÁµù2ÈÑù:qøQʦ QШ|¸[:8txSíÌU5ú:ô«ÙýJ,å¥y÷ Gz°ü$lÏv»àiÕ ¦$EüÛáO¾¦÷QbêÀA¤nI q1ð=éèü§Þ(T:pIG<}/\±5áÖÔûxãòoi_¤Ãæ{÷¹©F0lÅÖ1ÓuDÏëe5iUB5âÑkÙl8ÉQýÕ{çÌtÅ<6Ðg#<gZ·HßyùJØtéjA;3bìÄÀ() ^ʪ0ófp4(ǹ<w/¹w||¥;¸Æ.e¿Ã®ÎÞÜÄÌr²í£¦6mê±!Âò
©Æ»éRAv¦ÿ½g
ií©< æiaRòXDHw!ÿróÖ"ɺ<Ù/ázwÉ;oÂ)àòÓï¢Ã
æ}5¬Ò¥YQaàG£PEbY¿yê?¦2
q<Á÷$¥ûç[¦t@"xÑ#)x±üHªÊßL¿¨Â®·+o~AlÔl¾ü6þRdSÕûEËü%§äÊ4$!£y¨¤g ìz ÿì¹zowtK¹ ÃR(Î:EÚéú\*;ÕI'˯,ÓS4!7#Ï6b®,Áf®xlU%jòkÒSÂéݾ#.sW¥z©" ¦Ì<tsÒâä£ò½¡ÒúÂ6=4A}_QÝFï¿ÞxÓWtã6L'&çUñö2Ü÷Ìz£(ÝÁ'\£7g^¯öÍÐW`s^ï#/íýQvúúÀÆpóÆA«åýEêªù~[«=^áÕneø¾aÚÔ.?#×}$çÃ~ aJkH§®^ä￱øשÀìès»¿¼ê¼«=?}U½CdòDöTÓêåGÄY ç£T~{¨]Úk¿ Z8¿tµ¯N2[ªº4nz^oßÿbmÙ¨&i£lÜñ©³oÜ Å÷u3ýÃfèóéA@ÕòJQ!«»Ä8e6'Íà¬BPzvþîß®XãÊO@ÇDÐÁÇ8×ñ! +j>ÞÞºv2xñÁd&Cçì°³#.$7ùÔÖôzÖ³ã9Z¹#/ë;ëLGÁ½g&B6È[)ÒÄú¸úïçi"zös lÍ"8ÈÚîglÁ®={r¬øHÐ<×ç¦#«=
#â8mÓDDm´¶Òùõ¨¸#.öð³··¦}:Rn`P°þ®Mh:¸uÞà§mÀ~#/cõkú÷=cåïÿ ,s5¥TÔV¯3Å#3Ò±*(â&zQqdUAL¡ªéüjϣݤ â`¨ÁéÉÕ§vö=&·«} 1wd¿[&\2¦/Íé1È߶Ï#/uµÚÜ#ï¤bÓGáôÂÉTSææzPC¹ÿ#)ìUoü5úñ
½¹®\¨vÑÚ%ËPöùÑGªQÄب%û9×Ä´¥öÊçãMbüÙ$6Ñ(üiÝ!7 OZü¡î<ÊÌÖÖV(bVõBøRÊùm~uÂò? ÏW5¨ÖOVcµwÙçQYÆlck ÍðËWÜçl¿§"½ofÚE`Ùf-±dÉGåj}½U&<*YÁ¯QiJÝRx7=×>6¼Ýx4 À¶#/å5âçM?~äß$Þ#ÔÁÉTãÇ.}T¯'rÅ8¸ÃAôx@Pè¸s®wábìm (¶ÀB#0[²óMÌ&0]ÄCµ£¡G£ÍÕªÆF>ä5ß3Qýyã#/³¢vT¡ÕTÚÒcùeld/g²Êba¢vbYíáAöí#/öpyÜm,r2óÝTúĵ¬V¹¬mÕË?XfÝÙ4ÃTÚ#.}ù)qSA}ZQZï§éÂYì|ùÀë'®ÈF>öJz5z÷[R°*¨¶]Ê>§=kX¢hÐè²V-<M`T¢i¦¤ :5 ÓEðÅe ©(R¨(¢7WRõn©*JR¯<\®FVi¢ `%1ª²Ëúû»=1£¤)ôw|ü +îNjµÑuÂIyÓ¤Ý$«VßuU f%fv!Ë¥ºï»íffYÝ4¼í$awÄø³zMq3Iìßöüÿ4IÎ[Ö»5Ñùj×ãÓ».ß:§°ïæm_{®h¤µ
±É¿ÆVE½¹<íÔÉÝL1¤Î¬½Ùhm[¶#/9º)l¤J¢j
Ý$nMD»dYdoM£_=ßîvæ#)x~ÝX«WY!8ÊÞ¶úWZ) ñ˶ÓoRõèEOYÏ$àóR£ü\_Ü¿®WüWê¤:?ªZ
ê6Lñ±£Ï¬hƵ;4L÷á-?¢s×*r¾ªÓTbuúëÔ¬¿/1Â|Ý&Ëgí¤(ÆG#.ßf}Mê»%®\Ü×G«3·ÌU¾¬;g«û¹Qú·©uôïvڿӹܴ+×¼4&á4SöNºS|Y> Í»¸«#ÛõÌЧt»Í©6»hõuÊ->§Çßø½ÛÇoõöâë̽Z©{EöÃ^³äÈÒm%m¢¢x(6ßH»k@\öKÉ+uý« K<¹·PùYÃöã<çã¶Ìô\0úTå)LïWÖ°ßJø5e
3ävi't:«Êû^w¡¨fo~÷Z(4²á+á¶#)²9eKuoÛ[z³-uð($!ݵ¨?J°ïæIy)Kyê!w6¿AeR(;Cój®G#/dÒ_LY+Óñß\¤;+Øbï¢}eYØñ>ZøÖ@î¥qFZH×ä Ù
KÚ µ9z{ÊТø#/M'cxgcBªAgÆ'Þ×Y¦wå^(#.ÆCäã#.Pk#.Î,$Û]Ðìæ±;4é^jÙ¿Zr¶0luÎ¥²t5ô<VúÐЧér$³ª»øèÝKÝú4G7>õ¥ÑË7Ä̼Lß =·¹ÂuWE²Uï¿KݶxðÛ_£)ùÿ#/ùÕBê8rþOúx$ìéuR¡GK:<HU#ö,µEó_Ù®DÜìc;/D³íªÒç£ð(ÿéY{ºlÝëåÒ|$¤0}1Cúpóû¾Qâ'÷á»KBk :H#.C«ûüÜûíêæJQµRöàùÞsâ½Lgò_m»I8ôÂë.®WlFçù<ZôÆWæO·ûc®7øUaP_2ô¤ÊòSTZG.æxD|Îó(vòî:ë¤Å"ðöC®ïóóxífºiÛé¦ì¬[M
5_YiÛ§ÀèÉ«9L|³Ì6,KϺº´þõ÷a Ôó2I^´Ãf-ͪ«E%
#/¹<w;§·Eæ]ÆÝ/µÃ;gX²$
Mðbrzc©Ô~ÒÇ´°ÒÓFvÈÀ×[Wv+6Ò¬É_§Zìwt̹UT¬°ÆnüÜo%¢§Ô0²nqݺU0µ·µòU*â¥×\u¢ð$þàÇ#²Iû¶Õ¢tK\4ûËoyXo:<ìÔÍùý{³ñG2µ\t¥§Gÿ *Ý'×þå6qÍJBa2m¬c5ÅæGɪÇUÐÅɸ»ÈïüûÄb¹º1Á.¼9Ùï{30]ØÄØl|>éL 0I±¯ü®ÓÖÌNÖ«¤¨üþKwYkS²õsèÞ`u"I¥È¸6ÓËÒl×meQËDvzõz}_U§Wfãh3Ïs»÷}=É´®EhlD ±3¡ËFCc£Ø|b¨háÇ¥UL*ÊÓakÍ[3ö]u¨vÞÒ`ÒUrn!ªLÀßóýõÅÏù^ë®(4h`tÀÈsR>{e;«Îª`1íTjÐE =¯íA!õtVùpqWÛ*ÔÎÞSµiWùù\÷¨HU{®áÃÇJ¤gÜ_É3)$⮯½èJ½ëÕ*úAËërb¤«p EáѸüLHíð×y´'~{c;um»D®Mßø;·ÛTËu±Fìêµy xcnßM=4²]dóù#)ÿm<ä&Bmv6z»óõ9mA
v^®rjQêCÎ ¸ÞþéâMµYPö4?
ãuÒwæÕÆRV7\ÿWÅ<m¿Å^Ï»Æe{ÇäòàÆê³TÙk]³^2yþ$ÿOôÛ#/í}?íiÓ¸û.ÖyxÊæìý^n©dT]1³Eùè[&_Ǫ¬6(hrBäxÙ¡ózñÄ0HÍþßw=³ª£$2HÇæ?È¢öˮҿæçFt¶fÍ2²eC¤ï¨UªGû_l:¡nî÷ÛôVÝ»áBq0{zÔaLt¹Se¿Ãøçé,ÔoT¾Ï óÞWl^ÎÔ@cRÜjKÏdêMmýHvhíÇû}M[5`ÂôçÚ»#>^lÎðÖûfo #)Ü«·»_ÄñðöÀfW1¦!´-8éÏHþg¨25|btx;²ï9¥?§Ö3ðëaÙ4$u^0x>ÐhlËãìgï¹i-¶]¹7åÕxü{þ§H@û|)_.4´'?â
2kÖÃ?=Nñ~96àhê4ÆúESõ]5ÏÞ.Ò=ñþþèá±}Z¬3;ºb~[}íÝ>P4?EÄ[c&´9÷;Q#¦çÒò{iËóý¶qn,.ñ'º#/¥D0ÿ¯#/j{äW¸za«IÅÃÔj-;xÇ40öýùÅj4Ð-DZp¶[êÁ~êÑyE9z¦ótÕv+ÅV¾×úrÿH,ÙÉÌ.¦F4Ï¡o²ºm4Ê#.Ø°MÚóq]sû,Ccèumîäg#âU,z¢éü¹é®ÿcºk\.L!#ú~Ë#.mþK楽cúv]îA¤u&#)íùMÿáño§u¥Ðê5)hXÙÖ#)bE#ÌuLíeD÷"mÎÑoCMK¢¬k!SÇ0öèê}Îef»¨%sÐß;ÀAÁ(Q^y¼=ÝÂo²ºjË'a²aØ÷<wÑÆìzbºFõaöïXΰäC¿v¤Øè$LLÀâÏÀ#.UÑnúaÄÕ´aÔZ¬½åHDË»ûÞÕ¾þvÝ3}Z&6é<74©êUܦÓÀ×DÒn!Y¹"i@IêØsá÷¼õ5ç·nniÝÙÚ¡ÙðÎ7¢¥æX-$KY>sòûÍkÜm'¢ëª4ÄjÐXÎo¬Å)#/Åf3#/-Ƥq1Û4ZÍ6´Ò'N÷ø^¦qðºüü%D!,wÍP%£`°ùã8o°ß§QÆÿ·ûd=ûä$ÜvÜQ`ß>Ü\#. Îb§û^Y;I¤cé8ã°ù#.BÔºË;üíØfÌ°DÌ 2T!3÷·ZÓéyÔ0òÜëßÔ
'gÏê^õÛâ|bôvéCòîZ)63ðÔ¿Wöt3ÆÚí§×ü±qéK¥.`M¹ØEý?:ØL+´s¦ò8êlLhÏZ¦],ÓÔTq~Ñ»bçÎâÔóåÒ+#.æz¶F-ÚåâæoÝxì`ÌõM4?%Â4ªaB<Æòé£×ÏÔÄÜüS§°=,óÈß·6åÂwÏ]ôbôÍ{oN Mc:¶be¦áÈ#.pöÌ(éäkÏÅ4j 49³/;D·Z$:ÈpR)Â2¨ØÓRÂI98h¹·#\åêÄz£ ¥Ub¥çÙô¹Tþ_?±~Û=®®Ó½tccÍÈHâ*½ÁÑÜÛ½º Úë#J»)ÂVäkqZ×±;ïÎæÓ${ôtCúuI©ÔgøêîÜ·×íº8st1øÿ¤Ì5óÞu|=dëEr{^
¹r{o¨!ßTÉÇ_UÆÙ7(® þT¢\¾Éɤ³ÔnöòÞL0Ý5ÝWÈÓFvì9nTX«Åðñôùîtk_~·Û=Ú|8ñCCCJB(±«ÆÚ¤Àx`ÆL>:òÀÍmImª(¸$]JÜËy.a<¹òÎ+#.ß;-%Ú$ÒîqC#/ÔQêRrIþÿåElÀþ"²QùaÀäº#
o"1ë$kÖØEÊb¯=c»c²êýÙªÊÎZ<#/¸´hn`»É«ª#/wCYq¢yóÓóFKn9¦µJ:¸Ø¬'O´c\C¦Lë¦7ñü{d¢ùó$Qßî#/yöõ4Qµ§fv×Tÿë[ã¿®´5q}y¾ÛÀfHÍzø¿Ç.÷÷M$t#/wfqø3ñ¡Ø¸":à1QÁÖmhÌj|o6°eÖh:Yо)ªÐжªqL oZc'0'cÔ¤ê% ØÌ ?1QëU MC¤Ó¢KE ÎUî±ÓL¬/¿ÃZM3ÏéS?v·§àAz°#/(#/00Åkç[x$¨´S65¶¢S%i9Í'Ú#.f ´¦³2C
j]âáè! Z¥{Ócl樺¹ZÒ#¿VáÇl@JÄ㫳íS½Ê¢¼Öå×
zð*¿2ºcv+l¾$ø"¤¬úgÌã^v¶ô|Þ`¹¥\·;7Ë·g몽øõýûÛ
Á¶#/Tðk#y~ZGý®Ä«C¹ÛÚ÷¢ÿ¬¾¯W«NY¢üÝyµfÈ:nê9_Ç3¹º¬fÆFF6164XDVBð=ì³åó¿¶z²n¦+Fª%ºUn]¢ÑÁpÊÃ1LáÏ52ZíT¿åIõÌÅ`(5"ÉÎ$%Çúa¹¬|·çË¢Xû>«å°îDpÚL/Ë1§¨óPÉL78=:Ú¶sê ÚAÐÛÞqOã¤åü;<uZ¶çl»úOÅ1>½s1C8ñ³e °î13wAH¶j*à?»¦Í<#.ãU6üdQ7Ôô3XÇ÷j·cH?³L<1ác¤ëk«înؽµUR!ù2`«_¼úã<5éÚp:Ù姼¡ê°Àå!ЯIº?R1 èÂF¾/R8d[ÝDp}ï»ËázP©ÿy³ªKLÓ´Ó(4íe²*Òè2×%¦ºa¬FÂrVí&cE{O^têivºv#.{¼f¨ÝPél"~2 cHÆ dF¦Z7ÒbÕ0ÊZ#.³#/$ÈMÌ"ÂÈÐ SW,*+)I´ÐÄlã>קffÜW,תWÀé=Ä[ùÍUî×v÷ØñèÆ¿Èè#ÎsD»OGÉùbÑq7"Ã8Õ_fúêð#9´.«ÛËNÌ=Ϫd>?«ìåPÉäm3 7Ô-k5Ét
M#/ìY5¬ß¦}¹AÐ8^Í&ó:Ô¿NTxÿXàÁ¹räC¼öºaaÔ±o#)£±R0Úz¡ð¿â¹º|rÔQQLÉgCö+¦ºvÆ0ùJ#¨¶ãÄÝL{Q)[Éã%k0_¶}}Õ»wJ*øÚIU´hÂdÔï¶%89<®«¥Ö§:¾;>¾8IÝ0mæI)x|S2Î1B55#/³ü/Q1#)òjy!¼ó(LídGu÷YÙ=øÖ3FÙL#.3ãè¯'®ßgùÁo|'hÆ)t:ãKk*RDÐîÔR~G2óÛÁ^ÌÌÖ%jcÂñ;Þ<Þmì9dÄoe+<îìG8ª×ÞÙ}u"¡`ëO,çò½`¥?°ð¢x³éè¾R)c.DýØøaSaÿ«%:ÒöLÅÁ:ñ9÷ëÖÿaÛgܪ`i#)×bÖkã>4q³+Óì¶Y~Q%euc4é´¶=*ΨhNa¥ ²(¦Æsm®m$UѶå¡2l¸m³EYdôÍi(b²P¸î¸%¤]~\sÌ>YÆÓ(@GIÍv1cjíuüÎÆn«);~!Sä|Uqff¤ff#.:ºþ|J4´SP}-Ìyýä¹q'÷sfÝ/-ß±Âøt1Õ=fF'4LÖuÕcZ6Ó|?.æüõµì=:ð;ÒdÝÚíÐnôswº·ËéÍüÕpòójùµo¥rfrØ#õª½;PÿuAéG+5:Ø»#/üèâfY×åÆqµúå Q¹NÉ9Uu:úêà;rIÞ°ò#/G_²xË<ñȬ¸¤ÂB6Ô8ç»/·DLÆûú%,¤â½y&S¦®¼iè½â{ë¼b¤Wäæj>¿&1ÏY Ög4Û³Ï?®©×ò@dЪ¥½;¬«Íó¹¦Å#/¦ôz»;{·h¿ô.®cÃ#)𼿦¿Å\î§gQpa´üWºu[øpüü4Hü§¤î÷³Zþ¾?ÃÉöýÒm:#.=ÇÃÕMô¡Ü½ÍÕ|G±Ü-¨l¤*^Õ´ÓG ðÀîÝåøm¶eå©d2Ó_ÏîÓCrqy+#.ükËhBSNXZ²"Ië¯cÓLè´~Xüo¦»ëOí³¡ÚZ³D!Ódj'ôjÕ窼cÒP'oá(ÞZF@²RF(K¬#?Oò#/¡ÕûCäþ¬Ú2®_¿ý_Ëçåðff#/µZ0; a+K¥Ìv¿8¿Ïº,ù[Ãe9sÿ~¡_Wzû¾»b,*k§kAËRY>
s&$T©bT]Æbi-*6¿SíÖ.A@Á8@äªÿÔ?ö%ÑãÚ`]=ßôd~U`W
¶
\'IÝa-X'ïCgá}nh¬¶(¢´Qfo¤~¶45ålMyÇ®w³ÉÈa¤5êHú,Kövý÷õ÷¾mõÏPHò·ÝnW«÷uÖ×ÜõÔFÚ#,-x=áeeÿV6FЦsBØÚvÂbC]¢"x²ÉØ
w#.ñ0(7Ún?âü6us:ÌÕQ²]G6(Ðm8&ðäjqÙÆеbb%ûÊ3©ëmDÿ¾DL~"RÎç#.PQ¿a(y·Jeß°üîÞæx"R;æÎͳªm À!³LScñ˱ÿpO¦%3¾T}IgÉ=æ¼V¹?[:$?q
ÑAC×ík§¥\ Ûí&1JÌ)$8³¬ÁBúâ
ÃÒUJæñÓ·ë7=ÕÐþ$Ø¿^¸é\H2* è¼Èf7Ñ1ä¥ÈzxÜÿl&©ø?Vc¶%ü°ÂÎ(óÙT¥'Ù
?wì¢ÖOÕa]YÒ9fm!¡ÓAzlP»³ýÖ)§=°:Qïcåhùiïü]öuïô{CçÌùðý¾xþúsÛÄ]GDº%乤X^:~J[øÏÏ_ïúðê;ÉËк×ã}z5Jr«êªÅ÷Sç>"²2ýo§ð¹óçùú-%¢þþæ3²¨é¸îýôéA nìó"³¦õ¼nÏ?aW×YDë¬UB³£>Ý},§ë©Ã¬U¶c~!9ÊõÃã-ÙÛQùâ¥Ém¯ë??®U|q»'yúe©#/Îh7hÙærÂÔ±IØ#*Ù÷aØÊGñgTÐj^3¬>f£ÚÎÎ~].K£Ø£L>{64#)Ô%í3#/NÁðñ2EQ5|&ä~«l«TDsêè¾ÆS¶;ºßVZ½ÛÆÈltjl9§Þ®Y*8èò<ËM:U*â#ÜkÐ}·oåsÓ~#´ôJ ÷)Í#.·qåÔÒ!Òn«øêö¯²ÈRÞ&í³DV±M^½@R}¸´¤j¡¥yÚvãì\ÙÎÂÿ_¥øjÉ¿Nµ¹NÆS¶Æî$ðèã©ËÆôÔ9m&={p#I¯·×O'©G÷ýp\ÖÙ®,7Á°éûª
±pR?¦>,ùÚ]+dWz"¨>Tà"NV_**5Læ,v!c6xØ>É<|>hi§Éʪc:´!<DøÙVgáX3L 4B@a"Úg·÷½}g[Ïä ²sf´Û®Çb¸?Ó¦Eø'nC¸¡$[ì½ç±Ûõ#)È×Ý¡$±@bdu°iÌÂÀ)F"*«Ýíó&ÉáÞhçTF«ýÌ°üðR-C3Íx3þ,ö3lLÅUàØ[%6Û%,@.K÷¾R0@d¤Zç'¶2&1&É6 úºË#/~Tõ¯øpþóû»ø>ú¹ëöy²o³Ö}>À;yíëÉw·Å}¿#.í¿ß¯â¾óç5Ü`÷éÂÌMò5z*¤ªÛ£ý#ÐØgyú¶nÓ¨ÃÇn5ß®¿ÅJ³çÛö½Ê§ý+ÏüiUÇ ~ÿí1Lú#.½ÜÔþü;é§LZ±Ý©ÈÓ)b½ÑC~2Î|Û»þÍÜÇ/Zú5ïü;ú8õrÝöSxâ?\⮯NÝ>~cY¯ÙëÔz4iØ|2ïý±©rowö|=:«M+vTà¾Í^Mï§;z¶_ÊT-DzM§¿ Vèíëvô³súul<7òãÝÑú§ªôI½þ>ز¢»1Ó.çµÅãÛù°Ë³Ç¦Ñý*ѲVØGÉö+ôU×»~Þ©>Ü·×Ê¢ÔÙ«×3A>ÓØ]3èý]%·ùÕUxtïç¯^È×`å²Þ»)æuâ´³FïöÔí¢éUÃCiôYW¦«òeÍÖtm¿MCÌÙ·¾EPñÎ>¢)4Y)K¹çÇ#.»å:o[UkÏÕNû|jÆíÞíZ¿Xüü¾ý·ù/ÙíèòÜ[£=Ú^~#Û\ýØsðçéªÃ¢Ü7{o®[ÄÊ¢KÕà(Þzâ+V/×Îì£T¿Ý;êáÏò¦t"NK·èðk¥4?·ôLÝøFÖz½ËÑßú7¿òÈÓ¡ÕN=F0^l)9yôÙ¿ÑÅÙ§ê=$]§\YçÛ9Å[lê³ «ÿÇã®ïÅaÃ*»ÿÌ4\~<ò«³|·|^{£~Ë×áòùùUVNÝ?ÅÍ ®±ºrò>ßêÓ|Íß?öZ`Wæ³=*e¤ô5éÇÊúýóÕËë×fê#.o/ì÷`å¾£ñmoM<~Üï»_mOúe´ÿ
NÿåöôÕ.GQU¾Ëå2naF /u±µÏöûîÒÉ®sÆâí#.²§¿@wÝ£v;ÏWô·êÓjÑÀé>Sû¿¿íú,#.çëË£nçëç©özýµuø££ÑûJ|ÝH¨Ä¿A#.åÏ7ìþogÕþ_ÝáýÍ'é°t_m£k¦0ôFÓÍ¢KßÔíµ}âE¹9"7ÓÐ?71îø¥EYø§D;"ökÓÔ%a¥ö|úþ@×É÷UÑÞuuëú½|×ò?Çß÷z>ݾîóÕæÞAùäpî<+¦ê÷O´/ÄȮηݣnÍ[>¿·Ôµ!÷t·ÄoÇgu¿ËGQåîñø¬îý}\çgÃóøþâ)ÜzN®öM÷7cc¯oÕk=gðú<8·Ùøÿ5\mËé¯|6Ù«¢ÿ
gÄVkò·öáÕëôt+Ïm;¾ywióì<ýâ©*/hµHvGÚk_»àë^v±»;ø@ÄyùSöʨGë«lQ©¶+?´d; BèúèYäÓ°aݱ¤7"?«x9o»»«¸Þ`ÏåýÕ®mûþûdñÇ)m«î÷×t¿E®±*ßç^aml¾txÿ×Ö5hòvYñÕ¬}=Xý;
Nnv×ì²þ:Ï<3VÇ?ÏmÛ}Õõ¬0&gv.jãu-«â/Ão]ºõX\Ý$°ÒÕܺzê!«µ¬nOUMïغ³å²[Vx%ñí«áΪªzõìïEüøg_89_µ8l¯Ï4§(;yþØ;sEÚ[¢!Rßìæ53F&«fNEÓ£²{I=ÿ¿=
z7{Y_/f¿¿Ý~úªð{C}{BuJÂmVø+£Xh·<UðßðG6]Ý<piY-zéJøÃC/¬ï9\1ØÍnµpõz1ó¾Gír1nÈÌOøïXÕÀÄzâ;Euêý;l>*<îÓq®_-c,#:ë%$
Âýwpû½õÚ-ú85×è1/È·ÛîÙÏ}2ÛyÒgìñÉoóð)²Òþ_ÀfsB¶§`¾ô um£I¹íW-,« -ÕYâ|¿³}¢Ó-NûMùPìî²ë:[âÙ㲩쿾êí®Öç~xÞÎqsU¢=\¥õj|PTe9¦íYýý¶`Wá+úð
øòÕgg½·óüß5,S9×V¹»Ù(§/!xó8ÿ?Ý:nôþ3«åÏÍ¿Út7¼Ð#Îl_§Ûº8Aë\%áû÷ÆJÙ§ý}ùýYö_Ï·m\§òÞËÑHnª7Ãð¬Ë¹t~{³¿Ç×Øg/ foSë°èô$ùó?¶¢Á5 óä`¼þ>¬y±%ûJL²Üî¯ê¯ØÙv{,}k¨|þ«1ÿ?¨$`L/ï/óa]µs#u·!O*ìD<>ýb1£PaX#ia4(*I&ØácPbaDQSR&Ò0a¶rôÖÚÛZÜNK£2-`Òl4É4b,¸ÈȶYEÚ,Y+tÔºéåFºIJxÍ
ü±ýxÄâQ3EFßdD?¥S&Ó¬_oYÜÞ³!¹á%þ9guíS$ÏÓ4Ö]'ßüöµQâq8Òn]ÔEQE"YC äÄSÇCô_$t/=åØp¼X`8hÒ$HGJ²¯n
¡ldcf*Õ#.45v§FÊéÚ( ÿ$8ÉǦ¿ |8Ýôao
YiÕ¿ä+ütøS¥yO éٸ驪yìصëu*Óð®ß&ûu}µg³b_»×æ»?9é\Oâôéû²³iÛSroÕ«¿>ÚU_Íjw=þÚ}¶ãï¥#/°{¦²E ¿©½m&¢HVóKÖ¥ê1kÆyû=ߧô=^ÍZý]HkÙ] ý'_)0ÚÎ÷þ.0Ø`¶}º?/åǧ¯«*¹-·Ôߺ.RyIâ+\AyÑÿo<~/GìÕZåãDR]ßÉôoÞêý¾ñÕl¿M^Þ¿å_U{7nþº¿n¿fYé"v9øÉeÁó|®A©ÚVÿaàcM)IM9üø¹Aúîcé°ñÈË
&¸Á,m+G#.à©.¦¥#hxÊ4F²·#)i³ÜBÃ#ÀlLH0¬r %bs«IµÎÚ.ñ¦Ûw5§Y9##/)Z%¡`È¥M¦ÄÂ¥bi¦¸°lxVZÒ}1òu%{Rop¦ù¦ÌðmÑV#.×#)°u12(8ÛC¡GtqF$Þw§£X>#.#/9QB!Ò0!¦¹D»mÀiJ ³G\Uñ¨vJh à)"¨1£2#.e"æ¥#."N*FÈl©bl8$XµÈÃX0a°i&PNÍÖÂÙ§¶ö`4Á²Æ*«H!SÄH÷bÇÚ¢·fHþÖ(LÔUV¦®Â$Ô(¡Û:Ã#)h K+Rñ#/Ü(qN;pGÃq6µ%M3ME^nÞ1aß°ä})ðØÿs<ëoð\:$^}nöWø×¹¶ì˷˧ýU}]Õ|Íä[ððd·öçç(~+¼w¼í¿è?ø[uåvkõ#.^q®õÖ¬RYÏ-2«ë×Ý«ò¯Û»»íÃflݳG#/ÆõÛ¤¡QüÛÎC~÷×.úJR8·îcʯêµv]ZêQnúT¨P·«]JÎÖtèof']Õ}Iϧq®ÝrO®¯ÙmU#/Úü$å³<nå7ëfÑÇSʦéRçM*#¿/Ä.z\¾KpÁÊÞÀä7ò÷1|Yé#m»d³#/»ô>Ghª^¡¥aùý/?Ìþ}¿Ò¯-Ï»OÑÔ0#.ûÔ´<RgÕ9ïòÝQvYz Ås¿a뤳j'C.|j©¯ØaXÁÌ7·E2pd5RDs&xÉ:ÎõéT¼#EÙ$pâÄÁþ o¾täyg$C¦¥æ%ó·Í.Rèà[]ÉÉÉ=®EÁØîÌyS3AoEpµü=Ýìåÿ?M¯ÁÊN?l~$ 2Ë$èuk\£s1¯YÄÏf#.þ£Dç¯'³áÄÆ7ðmÛf°»WHl{#."9ëû&K'²9Üz57QÊ©{n~V»ùå4ÞjÃV½Á͵8ôlQ=¯RÚ×ÌVÖ>ºoÅöWºqÄx¢nr'ª^>6]õýn9½3¢ TÎìüºÆj¢#.HG©bj%þ+bSöJrT&5TãûD éÃï«Ñîü=V|ýçãô=ï¦?TþmN&aHBh«%#/ßh&4Î ì4õL1 åXjgLclm
õä&fĺb5MXÀDI<ÓcÅì VÜ+ÈÅrLÂÄI]ËA¤T`ÐÊ+Q`|¢E7NÙ´¡®\»ÑÁq ÕyU²Ý@QÛ½`³¶ðù¤#/R:ÊÁ¢Ðà¿3H×-¢VbëL{Dm,âÜð6@«A_#)ÌÉcà´õ¡j¡íéÚvZÊÆ8`Ø8´Ôé¨ Á±D´VJ,±#.6ʵ;RFEÎ #.S54©.m´Z1"q² c/©8LÎQ-#/ó×ÿI°çN.1yÞJÓK±TXÃ-dJV(ZÅJÃ#/ ðÈ<-[s#.>û%f»#.øºp6L©ÓΡÇ!ÑþûþË®3{{Àòç§ÍÆèüõìs#.V±½&tÑ-*ìèu"!AöC²$Oûºº,cH1î=¼:×ä×»]Þ»¸4åª<a+¹/Æ=0ö`~ê/Ð~ÂlåwAm Çó£0EHp¼àÃÓa|*LMj³è^½Øh$¥±0,N×R#N@"G`Æ¡ªOMééθ¯÷vIBN¶5¦¤Ê;t¦ÁÐÍJcÃâĺª4´h¡El$méÚ,]峫mNÝ=~ãUP%Y6DÑszhæÔDJoö~Ü~úéèκþI}]tüÕ_Gu5ãöù<Myxz¿Þ]{CÌBnn¢ûiúmâËÇh$\´ R76:6;nÏöÙã²L(#/R0v*º·bRÐ"kT)c9TinQi7D#!
qUúª&W*-c§KÀÅ[¯eK!¦®.gH²dÄ
9¨µL<LË0,ÊØ0ZÓ:Ò·ÀצzZyCZU`ùoF?Á¤e\ÊßÓæpï;,.ï1u6N½¹ß4iÈÈR§'v\$Þ"hàÃ|PëR+
ñI9±iÆ/&\~º»4_Ä(tN lièÈ(¡v)7dC¨BqÞ¥é9~xG{|µ^ÚiNº´W¨7Îd{!¿ÏåìÄvû^ùÌRýbfÜÞ5³U±±zÌ°ÐpêÕÎQÔ2³MØj#|ÁÅêòzMܪw±oÊNÅ˹p9SÏXí'`ëÞG³ =&Áä¼Ó0Ô¿L¤¿¡Í3ömoWä ©¤#.ÚBvnt\¤Ä´ÖY1ÙѾQ&ÑG#.Bã¼åcë#c#.MãjÆÜ©J÷0̬26ÈÑ!U*°!ªDK(VÍ#.²f:e¹«}nùÎþª¯#.w+_Ç¿9Î Æ#-4ÇA²ÀÛLÕmX¦S¥R ]dÓ×GÂuø"$ÀôR<¦aÆå$iökÞ\Ø¡ha)Û[çLo;Á#/S,f2ᦢ³|ßÀím¼·ÃfLÖ*£7G|¸g0ÂoFbJCâ³"ÔyN¹b4#.2MªNǽ!&hL$$z;u²
óãe`Û_/ïÀ]´íý8ók]M¡+Á¾_(ÖYâI, é¢ßòåPY;]°Ü0¸8-ùF¼!ßpG#qnÍгk=k¤C\#/'"#ª;kSFí|o]a<2EhJî+¯'M:ÞÛg|8,Ò¹ÀMÙ¬97Hyg:b¡Çü1¬Û8MÁ×5K°Ô¢I Ô!¥Ð,^(± ¶C´³nÄbú9%¸µr`j¹¶|,(#.5d àêÎLÒb0 ð(°úqïÆ"])¬± LNµ~6ðõZ\7¨tôMAÓeÆy~^öÛ^Ö'¶ò¹ÇöuæSãè±=í½æF[säå½QÃøl*Ñq|éb6/áï¬Yãqô>&3²G"ñ9_è"»«÷%ô<C ¢vز&´bfÐbî ¹UÜGpVªûÛ©·r©ðøe#/ÀeF1µU|S¶,
Ò1PI@ñúWÒ×Õ¦¬¼eånú¬©#)èB`I{e2mCª5äë7t9Ðæ0@ÓÄrCwÿyvËùCÅ÷§ñÓ½^ôÜ>HOØôv-ðñ1}Npg3/Z¥äµVËSÎB2#¬9Mýí,OyJZq\äíªÁ¢YDaâNm¾ûkÊsÕðãùбk<2C÷¬Ëèáâá@ÚZVÃÅ<»:=ï<4ó65Hk
)^áW±íæó#.uÁ*q²|bLÑ(:?¬\õé0#.àÀÕ¨7R¦z6篫Bc5%&çûܸoº.2tÍØ»·+³J(ì}ÚµªT9ݪ
ã§m¡(º^l},$AîÖ»èËa§-xk185BçÚD£°{ôrÉËcDsqR©k¦*krwd×ËBµR\Ú«¿ù¾çüí=ÿîí´=õCLGñð¶×²Xâe0Oï3¡U1êCÀtÆãÿ{óþ/2}ÞI4Ú±ö7§ÚKì%géW|KÈYu6¶psik#)H9r)&a4:8#EÝ<k)^vb¾×6J£ 7·îêª<þù#.sIÝi\¢W¿½EXwùZüô·&·Þò²\#мީÏ\cJ^©âËM³õ-#.~DsÑÞÊɺIJ_³#)/¦ªT¤æ ãSy,äMc!UË@,S(Ë>ü»(*=KÎUÊ]u¢IÌwC°ú´:PÒÄÅ^j¿~8^]ýÚ¶jl qÇ#.#.çá"tmãØ#.CËà#/êÚYu#ºe§YÎé8`®Mq¿¦)m^ç½ØØ|ï×"üÃWJ³¸÷P¾Ñ2íp®Z=$öàÉ´Ïa*°K;'=ÚD;¼¾.çôi(7úvçx0N<ñ$ºtàä¹d<±%¼|ð8»ûá«;Òy^=ÏÌ8û/sÖþ¼ÿ[uR$%åÉA]ºÌ&b©ò
ódrÙþäl¶ÒVØGä sR ñª-#/[ck8gcò ²ÒrŸ?Tî°zßè:<GÞôwÈ<(
<µdÕè;á?¡«¬yI×6*R´Q¢}IÌ'ªcbü+Çk3·!Ð påÁóÀôT¬µ "¦Ò+^Å)ÊW)<w¢Ó+u¸òi½ÿÃEGIJZÌù¾Ïë]×IÚÏSÚ$øvv{¥ü#/dü5á÷gHÑzëºV!DN¢Ì;xÄ&JÛÔ²ì¹óG©'Azb>]VýÁèJIíÁ^è,àz<¡ØHtfjÆëJ³7ÍÝTÆS=¦M;ô3ÉonÏÄ~7Ý{¬9sÎɦ ä4ÎÓå³+Ùi¬üÓÓ%!âNFÛèÀ»Ã½Á[¢ð)H¡Ììv¾Ï ñ}°Â³þiWÒ6!Ý-°º9ØULP!r<ã¹,£õÃ$ÔÓáXcý²§4¹ei8ìè»h÷©RÂ\T~µpUÅÞpºÂÍ&¨«0v QfºÞdrL=æ)ºµ©síe2mt:pü7\.9ßËmðùk'C|Æ6* %ª»_-bÿä#+ȪÓdû4[}ʲ³å¥wG²Õò ø|t£ÍÛÔØZÆ<g)·Ñq-º¯sp11$Nâ=|¹¹õÖbCoºUëäp)Ätî ¥µ÷²B>G³Túy·$±Qs ð¬ÉE¶vÖM_zw!î2å"] ©gÌà°~é¤$ÅðyJ¦ÚJ8ÊàrPGëf{dVì»''=OÓ·½±äíFZ6&ÇRø3Yùþ£÷Oq#.Å®êæ&¢½^Q'î<ÿVx±°&ýò?jhNâ/ðÕª»DIúv½»dy̸¬a¬ËÞÙ×-vC¹áðà<Ø1ìÓq/ùbôÙmFîÖWnv¾³æ9}ª{¯rµÈ\ØØ°ðáèªWëã¦çÑZ®T2!Çx!;÷N'ÅÊFÕ)¸¼îV
J¢+ãi»}[G¥#MVL;ñqòñùKÛê0ÙhQcË BâÝ®î×wÃvï8³/'/%2G<¿6S6°Cý¬}hÚp®MäLOctIÊ¡s
ÍθÒì¸ÜÇëºpËìøI(K®õ8þOä¹ëíy>»â}uÝtáñd ¤Mþ°\ãïQj)·N$S-Ød f¸LÀÁXs5ÐÙyAÄòÕ~Dã;T»ó>kº5Ê:7QÜi¤MYÕ/8^ê¥o}¨Þïäíå[]u>º3mtW!Ní#.Ç~Â#.Saà¬<ïm(äscÿ*|/+Í¡ÛÄþß¿_3^סuùkÍM!ÀóýÉ»¿kó²öî"ËtÕþåëÄi<SÌ÷Ç^Ïvù¯òDµkÅvE¶ßðM´Ô¡âF½òEç¬{3¹K¶J6ØE¥LØóíÛ\ê0á32>)GµÍEuª?Ü4s®foY={ÏëÄ·FÖõ0MÒ×WËSbG¹3{¿³ÝMST×ÛôÛ]ùÍÇÊlëàö#.©G$p?ÀåÌãÞ=8¯Neùëá/|¢T±Øàóϱ¤º"Yhá®ÈÑq¼zU»Ð÷ìç*J;©s$Süáçâ3¿64M¬fØÁhùÆþ#.¾f)½Ë7ä#O¶3uìÖCpv6øâìÎäQ/6¥¡µq$H¥¤|&z*ʲ#,-.X%fn)æÞ#NÒTüHÙØ8ù§G®5ÖjDé'í.J}ùÛHukdüø»¦øT?Ï|ç ÁQy#/ëÆS2Ê[Áï·#.øK?éý©_Ð|uÔ|HkQÇÍKc¿¥xnªÇuÚÀ¾,À¨/yâlÓ~8C Ø·HY¤äfi_7ò5¦d·Çù¹Ïnç^³®ÚS#/ÇÅ·v"#)ýÒ³ÙÆQñ-ùT^æl)¥¨MÍZ+«áB2çRiS¤9â·MM¯Éö>{pK¦&Ã1Çü´¢©Éü¢I:«¿ö}5]±Ø³Róæ]®éó~âÉ¡¸bJÙ+¬õON){êtx<âjÂÔ+Ô4d7pó4Ük±hÂÖºáÇz
3K«ì)NXKúÊפÔXÑ"
×ÍÔeú9½<ù®3HÕvvèQ[ywPûéû{BºÂùU}k¾9;â«6TS~ÒYa_Ç »_ãýðÛ,Ó´XXøsòñ$MU~WiÓ{V*aȦüÕ[y&²(à®Vs1ÓmªñªßßÊ«ËZÌÞ¹øRÚ¨ñp,*-tôè®%T¤§;ùÊE¾©gÝqMéÅþHÁkÄ\Gw2ºkö¢zù¿IæØø¢e'mÿtç?«^m]><õØ´pàålvÔH´Æ1¨}è{¹ã65XÖU'Ç,nÁ%}ik®2[VZSÊíôÓÀt[ñsº=qDG¢ Ùý^ií·`j®Eøg"Í JI÷#Î\ÀîÝ5%íúçõQ]ÌG]öçsT?ªkÇ~ã9$G¿Î³fKêQáù]~Ö`ÛyùðÊÆ|¬UêƤDSG**wsGf©ñï¦{C:U-®pZQN1Çê7çeB#/M ¢ysgê7k¦©Ö$[ü-æIKç8öÿÙìffyo/2ãÕq«NTK"-Þg<ñ.Õá92Â×k¦äÛ§gÉZ»Ç2¢¿]=%à/O«2¾*û÷Çn?5]ò²È½Ð*K7nkÎÍÜ®±¥Ê/èüa´AÎoÃ϶±õ"W¤¾mÓ:[Ó?_kÕõ¹XõìYEuÜô¼Õï'Ux6þ>¾xç¥öPµ:çùoÒ¥g{ðù»Îr=ûãNÏáÚræL¶#}ÜÑÒ#.]®»ê·ÁÍssánB¡ ¾%áUsx¹ée®&ìy»¢ªåîéå}K Æ¿Ö·¦cªÞ¬öÅñ»^»¥VáËm.)Üd-T¾T¸FßLañÓ|Öyn5xóùûN¹Éï]Üìäûÿ]1Å¿ÓÎ1'g 8<\λfÖ>Owgðkçç¿Íay`~:{ÅspÞHi#/3º7E¿ËÃóY4-jcʳïÖ÷íÄËI]ü5-¼:ó|ôùc}½£ëçá,>ì¼ùò¹ózx
D:hv]ËÉæ¥Éü áw?ZQ¸W=:ôyj=}Î;ïÝõzÖ}÷R5þg¶HI4·¯]W¯m¢î³M欫¯9½8Lé¥×J¢<±¬Ðþű2dsgn[n1*é*ÝÈs§¦Ü¬wéÔ}¸èɤԯD_[Aå·3ïç83=Üüã·§Ö³¸Îwt\¯[p£¯
G*n§ó¯Uz* p ñ«hhÕjÆ5jÎ-×˯#/±^Ô¤<¯£(_t
ç¯E£äÐ\o9Ád|öNº§ÏM
t«K|'#/k¥ëvÛícõ·O tñ?Á·§¢äܪ"WÖg\¦ÕµÕ@pÅWµehÀZ¼b»´td<êeO>#/}þgJÖµ³ØQß×[4G¯}Ã#/$dËôüt¡´Ùà1HRc¤"Õ.iÞÅE¢¾dmº**
$x>|r²ªø]ogàµ#.Áôxõý#.o¿ß}w¯¢®yè¡ä-ÊO#Â5ôD-X|Ua-IêTÑÐiÂz²"Ý*å<Û¥×U³w]d¬Z']#.+,4çÂêêÉèúÏÌ>?wNþaNüùzFëÞ¤Ë$®×ƸÊÇ_èÇmÎÙÓ{û1ÒC°ºg]=²OáFºÃ?Î|TßÉEwÌt£,ªúPªz£ XXÁ;»â,ºj1TRÜúêëdÉÝ`HªæØõg¾3X£ÿGÖwÉÞ*ä¶ÃªÆ\½Ý©é úô¢y{I lt/Ê7ßeãçøâõòÆ»È\¹¯ÎI;Ïr¾[wäÎ÷)JVmÛwÖwMÙÍeÙ1Zí»ßÝÉÓY;ñÖ'TücO§CÖ» Ë|øû¿f£]ú3É]ЩrâÅÇÏÆ!N3íÃÅ^%8PáS#.fIy×ipZ»ÄëXcöæýþ2Óï[ƬèõdÖõijË2â³#.eÑ®<=|!ø×z}Çx³9;u®î_áuÛ³Ôû-Á×À}9m!eåãëYCû½#. [D|Üß%+^ºÁTú¼ÇX¬K»S¦KúºlfSBáT¢ìó7H<±ÊÝKÊí~H¬lýº\ú~QÎ:EF
¼?Íþ#/1yGØMûÙ&ÆÝÐDLõ,SD\@s=¹ñèÜ©l<Ô)&¸qå~ép<eù±ÛÝúü<5õÞâ[[î*®Zºï¬å¢ÚQÇ´±¸ÏO¥XÖx-õ¬§?ON;'渲É[&Åaë«tUejþ3¸¬ö¡Ñãnõz`º§}ô«-5=U«T¿ns²Ôü÷êïíCÖ>Kõ&Åå×(ý÷ßÞÜa_OO|íVîâÝ\¯bËEWó¤]^ç6Úúòvx¾ù;Lp¹7qF
γ²Þ¢L@©ú¼_mtn(ùjv<s,%¢çUtâR¯ vºf¨³ÑÌûeÑã:KFìqåöB¦ÏÊ|Zö +zbw4ò\ek»ÒC*ëUùz§Ï±é÷s-ºÏɳ×ëyjö+ËËÕ@âG¢ÈÝg(ÊJ:3Pîä ©Sõʼ;5]3~¯ÓêûóÎê|¶Ó+ìÙþPÓÝva"Htæ5ú¬f0'æÄCOuÊÕj#/¤¼°Eo7ß+®îì)gIJøÁ§]tÄTþÙ¹?WhÉ÷ü{¾&s°ÉiÊÙÊãK¸ÔÍ5ª{ËMvvßWÑómÖÏ6W»~ÍP÷ôÅ/kNªC'B4h£µÅþzñ¶³{åËâ'Éßîèùx¸+n7溸®i=YNùµx½S&#jæSÎ,nZõãU*µE2uv¥²odÝ[óÄWu|ñ=çé¯U2îº~®Þ¢RçnÎ/}xø_ß[{z=G¯Âtuq|×nº¨ë½Ë;ü0åó\Ñ&sÍßSÍ¿éGN»õ~ã2I±ñÏYªÉÞ·í[%}ÔyÖûâjeT5è!ɽü°¿9ü>Øú¤·¼ÀO®äúPøvô:Ïõ¼·¨TXÙVHBJ!Ú%a#.ÀÞRíAZÞ´×äzãø-,MA2IÖüÏ47 ìlFÛ¢þgÄyf?@eðf}s^.ÝÉê»C-)\W$kl,ÔíÙo[<+<,#/i»¥ï¦5Ëgξ%uFº¯ÝKyUäÈc{ïÇ="òWE´Ëyo§¾¾þ¯~k<>=2täð{oÓëÑ,FC ËߦTÔi,CUÑ_#.Ô«ñÖ|e¿N^¨½¯A5¨k¤ÉÅ4fÚòvm´f4:êîÒ±=Ö½u¬÷HÎO»½oÑ`Åô¶:ébé×tQÇ2ðùOýNìVtÖs:Mè÷>WR«ÓOeA¢Ø'Zí]ßíu¯Q¢JQ».§´bD¤Ô5Tª¬ù>~n`|æsK¥ÖåѬ¼¦|'Fæ¹NµkÜBí¾=Õ>©§NQj+Ã=$ C KúHY:(ðÕ=ëÅ~'Ç^3¾õJsÖè[Áñ$uéüÑéð}S6<òj8>
'
ùçÈUÑEDYµKuN¼}0óov÷SáØVú%Cr<jye©lìZ0¾h´$Õwõöíåëu(KÑs¡Ü:ÜmÈç"ñOzï#.1¦ ùh÷>Ô?¯ñĸ³p^"1Ë®³zû¤rãÉÔõekà]tfм«K Î&a:11ÙkµÂ5k¾%§BO½r©íÙSѵ§4úJ@䧤J:«EÏö@âêð@±¾Ú}®ÑëѲÕïÄÚíOÚKÚNjýk+ë}i¦YÜKKrÓSٷľº2±a¯à±È¥NqqËÓ K¦étãvzº4'to´ÌÉÇ~µñVÖ|¨ Ll|âDf%Ϋ~`÷nëï_BùÏÝN#*9z\$^IÂßC)
Ôô¹uò¬¬nÔ["â´Ììó=þyZivd$ørÇÇå7¤«ÍÒ0ó#/Ç-¢tÝô¢éôfæ lÄÌ/6
@ÙõNRï]!ÓBö½ÁÎxM¨Î´k¶þéÎïׯDþOé±¼&<¢Ëýs¹:lR#)¼¨±ÝïÚomŨ·tSõ"¢àunì).$ô^}ý4Oë-ÒUwÖRì/VL]ßYÓ7tÃ?n¿nvÌPÇC²c%SmøMÃ&¥£kí¹ËÖåXÉ!)$ÏmõgYwÃÓÐ.¼·êQÅùèQZYpíþîoìÄ>ã>Ìn.²ëzÓ5zqLñ£~ÜuV 6ü<±Î^ÏÚG.þ£¨BWTÏ;ð´øµÓº·]VaìwbÕÊ\}9PÖßYÇ
<¾åò0×fsHR/]üÿD²cØñ:ªúÑK|*ÐXlò©8´U§L¾ÚNÛ¬ó»ÕÞÙµ±Ádî`'{ ¤°ßxTz¼'yÎMÈEêSØ bîp¤ÉÈâ°*zî§ðÅö«ý<¨:yïüM¿:q²¶·Ü\Òf¹¥
âW®ÑU8Rw¾¼ñ©®Û¶µmØbJ½öHº§d.9¹¡IÇAkéÔõÄæ´'=¾·ô?F×{û9Þk±4Á2ý1ø}WÝäó4ñ®{g5Ï·vÔÌ&úÞ´~ÓóeøEáÌÑ:Fõy>+~êËmµÛ#1t<Úß#.W*T!¦12=Øþh¼ó[ÔU$ 5ºùÈu/Ïý'H3!hH ?*é#/&jF0À¬ë¬9Ù@Àâ5¸R Xþng3@§"i#/:fõíÉß©ÂOÚôØûΦ ub ÈRTÃIQûåÊ÷ÔfÝü¢5¥qñ|üFñáÎh-¦Êê³*4¦¨R
ð&Ú
KªAûÄÙ³öÎg÷`¦_Üô´ýA1f>ï=&Tí©
Uþ'6¸Æî5Aö?ª<é"¬àÒ*\®GøOMp\
æOõ³74_¦®_hªîNêúErdù=D®Ô>½ÍÙ»¤¿«æøï·ÈÖ>T|ãåøC_'Ýåæ,Àù
®vûXe÷á#.ö¼yZëY¥·'«Eභþ¶«sèßYÑÙTlaO9U>Y½µÏðîàÇ¿°ÏÀ1pq ´A6BESéx>[ñîgcH'êýþüUÒbï"mYánñԤ!;M#/? @ôfÈ<£½uôàsmkÚóp@îñÞsV_Kýýÿ'LRIâìFùW#.ÆþægÓª~¾5Ik¨qF×KSqñº¢ekOÊjªêøH+@<a9Ù¢ }Û¹¦4 h(=Ò9@üçÈ0þhù@òöBykíÒ=ñç9ÉúvòÅCÙæÅâtpxFqyå!û¡ª7¼D·ÑYvÎnAu#)Õ@lbÍE°èjCø D¼zú:aäFÉ0ø@{î¼0Gù¤8Cùõúl¦µôªfd 9×ü{#.¬T2ª³à÷ÙVçåFoªhxTÈïBÒé礰ÀDS{|û®ÁíóðÛ¦úkµÁa#.J*#4«àWµ¤µOJ7ÀW^úâ£7eòÞä5ýºváðcÒ<¸Xhla'ï#îàvxYAu+ÄqþêÍÉAîU³
«§îÛÓ±´ÛLX#)°Â
/#/:Pp[å3õÕ1ß4l!¬¹;¯ïBöª@ÈúXy8ú9º¢û*0,yÍå9¤Ù4AÏ6}à´wF:4&û¯u;ÿá×Ë|lñwúHÃЩJB
£©^yÚ2Ì"J=£æÞ§lÜõ7Þ¦@g_`ÛN¦M^ß½°ãK¶üGÕ cçÂ-7ÄS^Ú£T21!±KS°ÜÃwO%2Ò&NY!5MPRùQØ9Ú+¯L°è?
rЧ£}ò³hÖmÉFuÊ«Kê÷VýÔ¯føîpxÐÑ47ÁMíA7}#UTE*Äá)G7#/¯°õñwA1ºnl1t°LØÉ~~×ü¥åuJ¦®\¹»ññ±°Îy×Ï#.¼ªÏâètþ#/ѳ=¤Á$,PÚVAÆÐWÒ8ÈyÌDù#¹ëïï3í-Ðß!ðïe~tÒ1ËGm÷~þwÙð½»òIË?C¾Ï-#.Õ#/,4¤Y9&.Î8¾Å ìÆó%0zòÆLzö6¢zúüÐ`»>J'rJÇÃ<¹*Àõ}h4Ìy]\ùÍ2eµÁ«ÖùV ¥¬xÖ!µ8ó.:éÃ]H4¸ÏêgU¦Ý']õy7Uv:V`ó¾í5gQÏÓ¹0}9kÈM:ÎÓÛN#)Õs¢WÂMlur×¢·Ç})m»}n_9·A5¦ C×uóiCt¶l¨Ñ3©kõe˨ÌiÈÅ®ü¹¹<â,ì¶XGqVÂLLĦí2zgB߸ªøfm5Øe"(Ó$êsîÆÃ*8Åwn¶Ý#)Æ{þßMå7)ã]Ý«KÜRµø¸ÙU}Ô'L(ý×ÇnÖmhÎgô.tõpWFd*{34ìƺ°Ê¢VO*ô0§ö¬ãJ0êm 2Ùb2M##.³Õݺ»ÕCð>¬fA¥îÆm:8'¼ålLb¿Q;v;±ø»9O¹ûÙ}¢(Oª¬9ÁÕåüÈ`8C%·Ü6@øг-d©Xjµø;åVD§¦Í5¶uêãÃà³À×BäÓjcN¼*#.rÐD¥« es1CÌ&bhضʹo¦Äh¤Çç×~\(v[y°ÎÕ®f`óÏwuÖLCµfRÆeÚ `ûËBÖåË´'gů¨ÌÁ,]õ±¡WQp#/ëo%*7ð¦ÉÀ#.ù(¡4ñ"r1ÙËMx§È}ÊBLU
ÆtXa'9ÀAP¥Õð¬¿]=xâC³M¡Ûs£0#)ÂÈËwiÞ^$chB¹kÃf¨Àn±ªÍ½Ze«Nô¹ÚÄNò0ʼ¸BPÒªËP])ÒP·Ý¥Áµ7×ÚÄbùdIi5:¹K¼£¦!úkÒƱdïvýJ1A¨¬òFpMmsº|¹ø¢$3$9AÄ9$Ìyº¡XÝÆÄæáãi¬køl<ÑÑ~S$p5½ÔýaäðuìtÄ#/¾k2ùõ=Sa{ùÆ0Í7Ýã{ãßÀuCÞé×Ô?'تNÃƨ¿8ðTråRlpêaÇî×LàºlÜÉ{{a·ÒzØA漧r¯3;ø®Ý©6#/¦]\Ñð1mGe5µ ¿$ɲ篾,Ý[ñ±½!Û*®¨9l¥f*¼5ëËÏÝi-Zíh³$6à¹Nfú³ÀØt°-×Ü[Á¢§«`RQE«Pðf§^4¹éüì°â R_q®~b¼%°¿:õÙZ(øÙ±t´·ÆÕÒÖ³uãÌUn¢ÅÝ^1× ZÍ-A¿N55ô»L4ì8Ùä°f]ô:Jzkkv8[o®Æ.%B÷Ö#ªkT¢¥°¦Ó~¿Ó¤5g^[ó8:lʸtØõrdwf9ÊÝëF#/Û¤NHz.Õ$ª45p*¾öÝ#.]òVÚÚÕùZbogø&t^²<ý|xÇ#.¯íSJôeE1JI.9®lp#.Ði,2C¿ìûûQ¤4;§F´|àÅuíVþ'y½8¬è1Oø{"DÏËÛo[ûp-`{:*÷:@ïe·£3-'·&ýH9&ëã}\¨!QR'Ñ|?wþ\Px!ýÇß÷µ9cS{½^Éã}4v¬:«¯¹SoÒàÅÓ¢`xíSvÞ®íÍÐøý÷È~«a&ÀÕbv©còÉ|]ðÃCtÌOÌZìNµÔ~=~é¬gf³BsùïcùWÈd
ý¾òÏkîJ!îî¿1ÇeÌôáXLU!.î(×52ïgj¢lÇAÓ¢*T"jx#."V5ùY/CyË]ÆWÔÚÄÄÁL.è×#.PÜt!÷Të5ʤ³výÇá"¦üè7881`sóq¬¬éóòSﶲ£)É÷¦Æz?=µÍ*¾Ho8 ¡æÄûþTyh7i¦ÐqÌ9ÈgûÏ.E9½&Óêvɲ=*ö.Ä94Ëhßúª$Ä&b¼4|ILúq=³f
ù¿x\êòûc½>bG³ô'Ô!6#)¡9º}Hi±#.ûöÙj#|@Ýö#)Å:Ô¥YÑ#2¬BiþX9|l]baÁ!@òÛä±¥ÈÒ(Xatª P¬\öî%ÁÆvØBº#.qÙ±;*¢9.È V$VO¼ó·Ê¯×÷fûº6üXâËÜìeÎìÚÐ}9CBO R\µ¼¦ù×S/@Zs5ZKê{×Baa2y&!g£ ØêÁ¡ù¿¦·7'õ0ìùhðzGeãèqN°ÒyÝKRlr-<+ÄLÎj¦WPÊ£Êcj£*ß0(ÿ¶x¬êù,êWôÜG[²¤6¾HÀâèË9T3¡´6{\=9rÛ%ìvb²×ÞdÞS`Ä*E)3DÖ±ÕÕm#.Þý/#/Q&¢3+U£m+=3*Ö/V¼3»ÂoÈ;¤tð7ay¼ð«`l
uÎ#/8!sE;M13¡ÑX.6N¬Ì#."åËÞ
ÕCvNá©e
'û.é~57¢tÃ/?¤<èq#»[ܵör±$[»±q`_`ûÚyûN½O÷(_qÞóV{Ü'Fqn¦ák>®) FDûëÉ0^Ú¹kÞê]84l±aQY`I$ðµÀ;FûØ0YEJF()ET,ìÛËY9Ì3k5wær·±è§_Làv¯>NÚWn/d´Ä.ÛL
©¨ç8æ;DÜ ÔìÓJ+Ug#¤«h#.¢s=*B@´;ù)xrBàn©èûØÌ8Á[iÂjÝÃmÇ ^1#.:äP÷¾v:ñÀúm5@æÅ"àÞVal:Æ ×U#.akk´3tè7ìh§sÂHZÉÓ3ï°tBRÚ·è&}ö: ÁBb¨2a1Ö1ÐÁTv *LÅí+s¾ùZ"Uû¬ÎxËðÍyÝ&Ôw{8JóÖx4·y8ÍæÀ>¢8r0P#/(KËTrbU(¤P] L$aKYlbHB¯Ö·1¶Èe°">úæ&$ÑáORw°¦`ºÕ
ïÒòA#.hæòaàóÓ· te*6åjik37yyB:0XÔõw¦L÷³å±9«.©ùéÆ>ý9[Äü\×£ÓØmâü½¹G¡-PÒPL@4«rzIqð/;ä¦)c¾\ÃHqÁ¤ß GÏ¢AWÀÈ¥H,]CpÅ¢&6<Ê°a¦ I5HÅ[öD ÖYäÏSwÈçIB:ÏÐmÆ-0ýxt#©#)àM´`9#/å@
,BR*rAÿ `N(ç¯_QÐVØ»¡¹gid2AªØ!I3¸ùÛ{Òbl=#AD¡Û±'vµ×9¤£2§0å{àë¯
ü¦*ëÅ!ÙJ²
´#.0 צa!¨·=`b«ÕîMÛ°Ûp×xC¯CU¿ÒU=aä%@ @ØfPÃÝïñÛï¿£ÇÀ9*V<ÎÙ~²ðO¿¨k³0¨ºqãZdÀR|£"`ßÎ\yì½&àÄ31 °Õ¦½Ó ³2·3¹è^H¢`Ìf tsf0j,ç/½X¯.äQ÷ÙS(zó'wFP5æ]²õl®[²á=ws¤ÛBZRc)R], ^³cÈ xøæmRÂ
LÐã&mÂY¸aËkXxø¥q#½:64Ï-¬åhG êTÃ_J#)¡$øÑÁ)=|wâ;¬Ð÷z:|gVüÎÎ0îã:q}WØÛtèoíi5È@ÞKê-í±òHª]ä{bÃGÇå HIuÃQ¸ãê³+Ñ£ó4Û#.ÍÏÓ¥´¦Âδ +ºCÒÑåèÛsÑ5v=ú [¦aRÓÝ.À©½0ß?ËÁ¿¤qÆÝ8öæûCþNâ¡Oy¼Ü¶¨è«×)×åÌÝÀ$Ì6A§,C~#/±\@@ìÏBþ·8ËöTçvÄi¥®áó>{xÑ]FÊH·MôyÑ"¯:¸¢#Hï¾¼ç&Ø¢Ü\êÙÅÇÉÓ³ÓF%@$àþÔI¯
@R" N{ü1!Î¥Ê8A:9+b¨è"ν½¼sæ#)«1$«R
!f&¼¤2D´=¡%7y,GµÚCäÉæMP³öÐçvÈ*óúa{¹·¾XìS§p>=Õ¸øfÃ<¤><)Ú$I
P¢aÝ6ðq i¡&BÍÀp}|ô^½½øK)[S£yûþ¸÷UXÉòû¡,Öÿ>:ÎïwçQsâ"ÇZÖ.ó|ôö¾¾
¶Ù;j¨¯q½Jé ¨m»¤I1{«ªÒp8à©ðBÁª¡(Ø%¶w+Ø«îõKf»(á&wÜ)A6¾WgÐïÉ3´Ô¸§âHJÄì¿"zúÔcæ'Kðõòå³ùmïmÄßå6LcÉÌÊÍ>(Õb1]JÒ¨7M!²m~Ñvpøo44qãCM6åKvñ/w±VÆ3Åü<~økj;à8¡¯-ol Ïr_Î`þEâ«ÜSCt4>?P=IÈ_FM!5HªÈÌvéZ&)lÊ«K©Ç]!ç2lE0íÝF0qäæ>µÓv}mgnÚèûÛlxk#.*Ìuðàaï±ïJJb--,àZÌøý°4ºm3³1Ò¼&íØty5·O9´[Âײ°·iÀ¼Ø©ðL̲:ñ%$Äf³yø:Ø!C;5ñ*%§#.»ãvóɦ©ëbÐcpm-ç<s àìb5&ÞªCÁó"hɲsÜÐDwçb0û4óc0e!pÚn)°ClC!½×uß%¼ZE4±hô×U%Jf]^øm_;/Oä.»L¨Ø7q¢ìhÓX¸Ü7¶æ$üâG«Ël:R^ûgC¿úæÓEEèÃíÌÖ
Ø`ÆcÞkÐ@ÜæªüûFMdaáܺpgGó/3`T ùúô*@ºú{QÉ¥
Û!øM.Qy3ÙÏ=c¢Wç~ʦÞYµ¨LlLnÂL,S}Ö]¶û®c5Öö#)@Àáö·#)Å5}]4~;·àû ýr4lx§ ìHJ#.¢ÄQcïíÓØ$Ð&ý?)`ZQÅoÅ¢gÙ£O¡*ÈÞìÜè*LÄÏkßÚª¨wñáé\y9Ð~H ûtȺ#˼ôÇÝååå¥ÔÍPÂñ_)äÅÐ>ÐdÏR6ïcßnÛ[ÒY½ÿzS±û3ôb©½ðuÑóí#)b@ºÓA<8«w«@S#)b±5hö¿·ìÝ!×gÔ§*¤Wù? zGÈR©Vûc´oÓèòêÂ,®°ÐÚÓh½}](lQò_ö°èk2pÆÖIk¿ãL¬òÞ_w»¬kùý¼\éÚf,¹óL4¯ò_>í8k"Ýr0ͽö(f½ò_ùþ¿Ûùÿb_ÎSJÿH§BP
#/ÈgW;'dk¡}òü×WuÕE'ùålºÆ|ÚMAý3$Ã0ßíU M¼þ(l6ù×#)áí~þ·úK`%NØîp¥ÜuvèlOù
úÿ#/ÇaYNgbgÌèýïÃKìä¢ñAü?'±÷ù:AÌ!3sõwsîðëãYÉMxÍ"ÁHcvngôIÙ&B?ïÆFA1%ddP§FS?¿%6¥HWó{ãýw~´Ú$ǬLÍïjßñ»ßL¾8dÏÓ³qØY¢WÙ02>wn¸µÞ6º£GäµNy[q]ÙeZO»ZÊk1f¶£~uÜÛÇØîõµÌd9ÒvèÇF»ú«ú§Þ¬êzåC°ð6Ûåò°qþ8y-²3¦éÐùV-±×¿VÏ&AÙ30GÃÕbFÂ-wEsùO
X¹Ûìu¾$!ãÓi¶º}Á»àã Ðrwr6L¦<ØvìOÌbaëXbUkÅÑóøôû3'úÊëøqÀtäÄ6Æ`°V@aÆÛS#.ÃêOôêèç·ÎVòµ&§2(ùýªËiØ<[C½1ÅK?7óÐK¿ý?¶>0u\|1?¦xmÍÓHlöyïí¿"Ø¾Ì µ¡Ë]ìUƾÈ+¼ªWëñð/? ºÿO?÷VºNmÚâÌÅcês®ÛÜÝüýüǤØo#/ëñ¬½R444XLúD%k0Éu¦Â1<0Äqk0ÏfsÁyÂÏÝøêx@;ï8ÇìÿAJ#.q+61 'SÒ^ßc3#)½oj½ÞÅúdNrtL{XoÄ).Ý@v#.åê¦.ݯÔB*3î'T°µíó~¸|RòyFØ3½>f¬ý\ÚÃC5>vCk`xSǹáww'¨ï
JUW#.}=:¸¾VK©Þzõ¬?·ø[]øÓ5ß³[úXJéÍÆ äøTsS½ô;º?^ÙK÷í)ñÊ*~ü-ôOÎð¢ë|§~,>DZNA43Ì9Û÷ûüÌ?Ïk#.çÒû=|ìcgA\_T©7¨ê¤qÎz%Õe´¥W"C1ü*{*Vz«¤·üzÉg»Õ³Ýë=^¹úu+à粯aôéú¹·"x¸Ì$ï®oGìÕ9Fɪëvv©#.¿{gGî\×ÌÉ.rX¿
ÿãçèÝYÐ^ç³WnRLS¯?'FOK!oÕ&>h:ï`l?÷¿xÉ- ËÌ¿?àåÞaIM;Ó#/È"HW}]uâu>¾[4êð׬+ÊdÈ\Ã7Á®Û,1×®{M¤°(«ù¸
á$ߣ³·Ñ7çÉ3¼_¯=2ìå_u`à}+5Ád§1ä¤C6ªIQÞëªú«ß0æý÷?~L¼>¢+7¿·÷ÌÄô À8¢4®uÝ)D¿Kòa^ÿ«ç l\!#·=½ÄÛ³¶-÷O2¨èÄçÜ]-Þ«®¿ä#)¤oÎL1NêEÒéN rc{|bVãÓñsRÇ"½T¼`Õò0þÑ~¼0¿}è¿f½g©öün1/R=þ\oá¨@oqÓ¬Ï.L+õúÃ/_UÆø_4°¥ØaN áâ1wâ`ËK_Ëä¼çXzïúØÁúuóõÛÞÌ̤ãu\¼7í7MÏB¾¯Õ~¼.÷#Ù§@ÅÌ$Û¾(Ç$y4§Óý7¸èÙ§e¼¸qãX1Jåöf6&ºÍ?3oÚ3KÓ®¬å£Zh&#/ãbý´üc³û¾_Ûüwɽ3Ô ÆïÍ*Ð(㩾8¾*díÕø<kÊËyx×M5üHµSAö+XÍÞ+ÌqTRÑદtéIß%=/úäÒêUQä°NεKþ4¸ËÜÿgI¾M¸ëN#[²&æ?·y òg5/¯8Â<µHþÉéßõ§J³ê,å·gd!gß.(tǶço?]nÉßhU¸åùÂb§0öðÞz=/\×¹1Kû_×êÓzü²ÐÌíâ©R Ez?½ºBH÷âå¿Tt(ZÂ_ëÓUaiåðAèS9§£ÅùC¡¦>ò·s_7í@â:ôY#$<ÕÞggÉÕR/&¶ãüWØÉ÷ãP}Ê«iµ-R'M°Ú,Rîÿ2æ«ÓYû1->}TÂÜqKËÞÉQ{,ðæ%ÂfÊ%LÁéûÝ&á±+9¤~[>
+_óO¿¢Æ3VÚ4?Âÿr:Äryázµí,þèfzuîºLzà}Û>í OdëñD-Üt¾.§½eG<qÄpù:¾¼øé%´hæ)VTæ÷R8[¦3Ã#/rrÍøÄ& ®¸ê=º;¢Sºéõ÷æè*Ý%ùÃÃ?~㼯#¸çgK®n}pW̬ù\ÁäºÎïW1EKyÝ%;òBë×YívN£A
OØ\Èêxñí¡|Áw-EDWN"P[@ÉtwbÏ_<yYie>;LHHB¹vøöñUì/)Ne`´·TFÙÂÒÃnXV[Æ#שàÜÛDc<VýM5)íñèÎ0Ý#)¼®xèz5Cê_·SËá?[J£'NÓ¡0ãZc»<ÛüÙ¬øwáÂRBþ0SQÿSåqW©¾tº÷;µIÛëã+ÈÕOgêöÎZyzüØ^-ûæsˤëë¿aøËOÄôð4^·ÆdnzN~Ëçê§NLT÷Û]ýªq,»ÏDU"ÄÍ=~ÏG¼uyÀ÷¶Ã¿ÅñåGã¼YìF2î¦IqDçɧØÞó°' £ß÷¦>èJ=4uu¤8
rÅr®Í;[Ì ðç÷slÃÛÊѮڢtøÿ$?!çÎsNï»Ìy3ÊÒúö0N·¬'ïæ^RX^¦Ùý8¨1Ñ?EÉûzÃdüpý#.T_OÒ·.L¦u»Î®+ Ù4¹RGÇ£ím3²?ÜGϧÃ@Òâ¥Çú®¿3Ó;öGvËüÑ#/@Öð }9Ö¶/S×zÜ
»éøYf9#qÍ«¹ÑiUJ DÉht¶þX¸¸<ÊééT¾|Îú\kÕÝñõÍÔ¶ß:Ì9àôòkâ|ß+éno¥qº¦ùaüaòØÇm4õ/d"U#TãBvM±=5kh?µàGjÚÆ ð$í6¨hÝ«(Rì5êE»æô`Va~(iñàõ* m¨ïªPö¾)Óa]$QY3}*mN§E¼NЩ®¿TBá!èH(m:úü¿¸Æ·
.ÚA¶Ò³5ígO®1ç®ajQåµÜíý\ÃÔ`¾Wºû¸fùkp'¹ÜÜ¥ Ëç]".Tý.¦$[#fÿB,ÑÓ±3Ö¸kÝpÚNÁöò¬d¢Xwó<õ«iÓ% ãçùláôã¿$z9À»=Gú?Z#.ò½íÁàr¿·ß#/Ì ¶~w #bµ]WD¥´v¡óëTÇKµÇ6wi÷0jȳ»ãl÷\/®¡ÇçñÅV4!F#.Åãz|ûøI·ã9§Ò"ÉTZ!ãP$Êt&®Îü`½eu»lcØLg9xÑ4'C£ò@Z=QÜÓV»öQ¿Ê·fRvÜyã\ÐR¡\gÓ2¡xåq/1d§'ØÅ\ªÈ©è»Ç£Uf7ZRv£¼isJÒîÃ&#/ÜxWJÌ=õâïÏßgÅêÉswwgwÖÂQÛYGÕ¡y2J1FfXI)Lè1¤`MÇÚ¹}åo«²®²pd½Ûûº·Yã¤#/Ð,ÇJÒçø1@4qÌ#/#.r#.j£;àZUÔìCùd^CÑ )Ø/oðJçü³§qW×ø×MÞ
ÆÁâkûúv äZQ)÷øü¦xF³=ty]`¨É2Ä"JÛʬ2vÃ;LÑk^_7{òÀ[30}Uh×ùOom_ÕK~ôô.W!Ó#)²»èé*{£»°È@ÀoýØH·óý?¨·qOI:üi:Î$?ãGþÍöÕ<Xó3TÏ PÇã Gv Ì¿o¼Bb;äÔÊrIûÐû¤TE?s$ 1æÇ<Áß)>®)*¤$̶°õ´A~TQ[åèzÛQ¬RòØH:Ë5¥C#)ÜSD¯Q[!KÉþÏgýæûu¡-7µrE>ù:µÒÜ5cêòÐú;FóFa´+¯´®VÐ2±Î¨Hv_8¢@@«Ë!Ë¥ÖWXtõúÏÕ]á§OÆCwg³D¦á¤ ?¨ny¡µËÎömlGæZ/ÖÁíöãsmôªÈù¾XàýtùûæØÌ*Gíå+Òv·Í)}_³EºNäÕõ#/5j<ôhü)ëEÓ¶vi×í7Õ[*Ap³ô±Ýó°ÏØE1¡ÂbbÈÓ+,bRF[NmîæüÊÒI©Ã#.#.ÿÙ濽ÉÛðú˺xy08DØûÇñj~4óå_Í¥)`d©9UYëÓþ'Éz¬¨5pû¥ØýõÉ(,I?z)óüô"KÈys<ÇÌøbW·ÂºÈhÞ©ô/kܹj1ÑI3})Uî^u{ ºunëd]¤³wÖ1lfdjDQ÷ļ÷~}·é¥;¥ñKNgnã#.Q¹Ø\_;'UMO@é´6>¯:Tn´VQØ·#±°1òùßù|.ôá;îlúÅѾbPwwáª)]^æ½8HTzzg>½u61T*#/§ ñ¨¬¥¥y¿ÄJ¥làHÆCÜ6¡ÓÌB T1XC²°,F(k?ãáÏ&~y*d<ËÆÌQëBk¼úXݬ1²·¯ØoáPëÉ.nâ{ÏÕ½®Á¤]۶ݼ²¾s5¶p±;Èg,]ã|¥~¿6E$#.3çxäáÉ>·ôéÃö£ðZë{n9°è¤Ofì`3¬ßuK¬`Ó/ÍÑ>[xHÞh½ ¡ÜüáÐZþpYòW4ÛJËå³2mtD¤¹ö\®áÊ3]:ÐõfEä#+M¿òeÝ>{<·ÃwnÈjOÚ[ǪÉØ|WGEüæó«AØÛ£äéÛÏ͸ÆÂYJ¦©²vXk®7\ûª åVÊôwY«Ëµæ$%ØËÜîsÍéü¸®¯ø3ÍÍjO]Hx2Ðòí«>뺥qª&ýVmú}ô¡9ÜeÆÓ½iézB^¾Nu.¬/8võGäüÎ^yJã Q?õ{=!íß½7Êv§OÕäýÒxÈ"¨&{zç:xè^«áαìYϼýN¹×ó]5Òï±eóûuX¼DM´5Òüq"WV2>Eýé¨5hy&.3-ÃÉSzgê,G®ÌêèßI:¶ûWjRx'<±Çó#ÓßsåUíÒE{óδ¬eQýü&éÂoèÑÊìàéÛÅ2G×:ÉíLÞHãçFiû5ZÞZgÑ®r#.h9{|Op±Ðk³[éÒji²W¡0uJÓ«MYí¤ùÝ÷D2`x+TÅcÅLü~û5íÚÛÆù\òêðG=ÙÄÊ&ç*Ü÷k¸×+bH¯*JÿØÑ[qNdÁeB¾¯Úæ3=}o\èaSìÌl¶ºVË+jÚªáUÂ_ºQÏ%SjwjÅ®Ó,3öî?ÊNÜ|WÝ<vÌ*}`ñYó·Æ¯3o£Ñu xÃ:tFª;Yü{0@Qå#wSºð÷,çô5T>Nß?DnÕç\n%éÜÛÎÔ´¦îÉ̯áFô¨0UrOÍ8ÒÉkQmÅÈÅéNýºb+¾XY</Õ#.{óÜòF·ªÜTFXU:ô#m`xpsÕ¾õlzmºèãÂ4þÞ§tGt´ZϺp£aùºEçMSN7]°3ÍúkKn=ä}fÍDJKºH르1xRmdÚÅ2Ñ_¿q¦*HâÔ)ªS]7ò~¿¤Ø¯ûOÔk½xöñ5@sW¤ñÜF*ã¿zýö5öÄm§ºÈ¤,¨]K¥éz#/ÜB@N>úpóIÄíÃúÙ©I¼ÓocPÏ#/KnÊÞ¸îq/t¾¿ÜV;Q³ÏmbqÎmN;$ÛQô`ø(s¹¹0F¡$ݬ)õÖz¶+µ3öHoõ§Û n3ø®5½£#ßÓÌÆUhÎÊÂÅ÷ÐQÀø½ggäúʵȾû·æÁTÉ1Ü ôâc¤Õ[ÖþJð7l§º³ÏËêÑjÖV]ÔÏ=0b*k5¨EÇÉì÷XZÅÉ[Í;7b+O/³4Ú5Ðäç%F²¦óhgöàã+uLY"ä¤ê4×жzOEÍíUJºàv>uwÁã§9&Ö)Îà åÎÄx;¤Sº;Í8èk#)°ò_PyõÙiæ¥ipm®ã Ý×IJae¤ðÕÒlL- #/ܲ§}½"ôÖDɵm¤åáÂï¦Xµ~7-·z{I³xw])¸b.y
M0«.º·½öð+ê7Ç}T£WFa©öRîV¶+µêÂ
¯>óñmM;´ÒÄ!»VîXíÏ;êèÙq¨s#.,iaûÿ½:ðûUL~}ÐS]ûVi*Ês»û)ÑÏt`ÖurÏG-jæ4Ù\m}Tý¸_©j&ÕÚÉs!3Ã"âÐFD& K¯!Wøm`á¥
«MxäYJNewøQW²ªBØ?g;°Ú§äEÛtt<wÞépgÑ>48\)6eªÂQ¡ýZMüW#.u6þ®ùJ½; %¸½©-ó5!õ&Lorv#/©&Éo2§X·E¶éÝÄ:gÌÔF>lª¸<[±ìcSuJR6ÉP¼Ç·8ËqGR¹@dNòÏÃÄüòÏßßpçQÍ Í7¬Èws*¾ÈÏ¡¥¾éÛ¨äqø_Úcê}ùÉÉìNTéo,»ANFðê¥$ª7DxµQPÐv©Jà¼ÀbTòªN¢îû6 én¾ÐÛ[üU:ð·£Ê»âº/ÒìïuäèlÇ2Ío¾.ª»¥"EThV!!àp£Ð#«uôÜ»iÈÑÙª~&³H95á®6×Ó"~AÕMÑײ±õ½BYáKÛÂèÞ<ägd¦òå2YãÐRÌaÝ]
:Ï5ïN¡j¼â4&µ6¢+5å*zl:õL»LîÑ]÷H0¼å}kñÓ¶óÏNîÇÖýPØxÆ+#.ÕÎF[AåEpô¬@ûØûqÈ/ÓË<^þmhh7Üë:0QzzyÉÝjNÙèuáì#)_ë»ñÇ(ýÖC®ÊwE¦û&9ôÁ\2¾ÏÒ=ç¿Ë«³C!ï ³]n´¥RcZå«ÛÜÇw^î.Æ_ï¬ÕB;â¢Sþaþû<ξ½Ã&öxqüÅæå_Þ=+È«UD®'~z/¡½iG¡ÃS³t&(Mø
µðÈÒº5åãG#7ÖäÉ£M$Û(_áu-Æ»\Ê%ª¥ÉCêÚäpÖÀ$ T}G5ÈM~)]Ö
à~;L#_»Uý²SðßMë;EùáåÙ-Ó¤Ñn#/1¸MiÙµṲ̀]çÝY#.ñq~X_v¡_»YçRùg#.ëªú1Äe}µ>Y8oÏgÜt9_FT·TôrÒLúìc©1sH²õ~/áÒRP;»Í;dÇg%+¿.7I]üoÌ]=ì þ± |õkÃSÙ£AV<4Ëþ1 ßÜ,ªÆ5qáNó}j»nM´eª×¦[1é6=0MRftYd
*bKôüÛ7~oä>?!ñýc:ØêÁ'ü$¬úDµÁßü^uãA÷#é ûnB½Ýrô³y:ìÉ9ç~è~æówdI©¬ÛJ0+ꢪ+=.)|}£N>pqaq¯[NpI~ìÏ)H\6ótÀ¥>8ê}o*×Åalô==ß¹û<Øxrý¯?¡OgñlmÜÖË"îx<ËFýFcêXãÓ0`VØ×\CHþ§kå¿ð}"ÅçTÿYa¼ÝR÷H`eÉØcc¸ÃÈpc)ÌÑç+¯ý¢]ïêÁá=Ù¿òwýjð_±ô#.ò³%Aa¯ª@éÃçôöñ$ü¯÷Ûa4#)ìæ¯Æê ëb¤;Rz
4Xiw,/ö¿Öjaä8ÿþsË~üw$Ò7Ø@ÅÈ¡þÈÃä#/5ÄÆ©©»`2%~®#)1`ÀÅLZ@2CÙÔñ#ÏW²Í©õ("`ÓiÚy0UîÅõ$÷{P³:É"bv`l þÏÐzGkæAô¾Ju{_ zÌ~/·âÁ~Gçy(#|:£§ø Öbf{ÎC`¹*h1ñÃÿX8*¼yg@ÓüÕÏ*|vÛ¤Dyüß»©äÓÞؾ#¸/ópôüiÌöÅå³pöwë*OK=ñÑV®ÿËOQ`ÄfVw§ÐÁ®
iúZ4ÇA#.k&æa®sHÂuxgï6aÖá¸æü\P¤)#/f©©ÔÍ£A_Ûýgñû¨sý*?<LëF¾WÙ)m;ök//Ùw°^IQ Çè?6áúͦ³0@aJ;YþýÅ;pwä^T('ýH(3¶@ÈÁØÿ#)ÏgØ`'>î¯&¸Ê-b2'vkC ¯`$?BÉHhiaÛ#/ûâO#.C<ªh®*Ç[¿ûÏ Òóyâ}aäMû±1áï©UZvTÂ"ød÷ Ä<@ð Ô7ÛC®1ÜѼÆgá-v±nñyÛ´ÃÉæ¯À褢j9#)s0$ ÈcXv®þñì}&À½´qL#.ÁÒAõ[®"L>!ã±Á<ÀéÄæ9ØÅØc)ÃÚa¶I::ë#/Ó+z'iøAúÈüþßfÙ°KùLËú¾^ͽÞÅ£'àdÚ«ÞdnË¢uñì2QæVì0RRHÔE¶Õþ¶oÈþÄ
KðâT1óÿ,.Éd¬Ò${ZBͱó7ÃÊåMx¤¥ýz®Yùμäk;¬tvb) C©%îàÚeLøÊÏûúñôVY}¬¢Óáiue²Âu§§.LÌC{WßäsÛÆçéòû@ùÙCGÜA±ÒWñ7Ø°nµÞ|çJÏmY²4½"*TnÓ¡:®ì"C
ÈLi§åß9 ¥ÅCÖÍøMìo#.!TQ#§OªãíwaÜI¬mm0ô!ÜáïΤL©ÂÖ¾ã·ÁâuÛ±v~ǵpN¸{qï0ÈCýÇÛâ êã;ÌÏ÷[#.æµ2\uÿ¤ªªz§t¡Oæü®æN6õtEg7né$°ïwË¿ÍìÔa°¾sÃ>ÌðM¦w6NÅ»K2mYºöð3âÅ;õÚxé×CTæQË@Õ'9Ï3RÆMBÈ s£cyü)×Mæ<IIOÖ|§¹*!IGùÌô÷@ Èýïø|üó>£Ë+'çQ'ÍþÆ\`T0¥ÑGÜnãû´P#.ÛÈr¦¾F÷Ýqã:¾bñ´"jîîÃòÙ}ZV¢ÏÜ´ éX²H=ßÍçû¿+újbæEdPjÐMü,í#.ë¹;çÈÄ?ÚxS~3þloRoPä7ã~#/¢àvB¦*ÀKÚOè4áÜ#ÕÖÒ#ÚöF}þçñ$8#aæÇäÎ?§ôÿ³Å¯Ð£»ØHIcBxúë;ûÜ<àw¡è<ÈèC°m°6=¼Ò¨nÎÎ`ý!SØ`9H§:wX>34#.TßêqÛ&3Â$åd ©S@"ùsSrñÎ'.æãäx>^³>Áâ]%ûÑîÞ¢b+«õùîwwq#/ÚÒän`lÚ#.ú8¶Ö¦oP=K2[àövÁ,Å2hB¡{¦&Ì}Ò6Æ,¦àÿ#.!ØB?§¯÷¡(û2ú8@»@W"MܲK4æü95i6uÍwÖs¼WZOzDz=Å®öÓq»ÖWFVì|R°cÓ&ââMqRñ2©eÔÐÏæÑrhm2ènØöfhåj)YD"¦+±à-8R|J7T\v¹Ê´e¤ØBj1±k6ÛPqñáö~$|ò6CH#)þè_Ïû@iÓDþÆvýl§»¸_§3·ÕÕcw[PÏò·×ÜýúY¤ÜϨØ×HOß8ðî¯ÖÈ*ª«t:HIrA6 I'g6ïîcÖÇw±'l²O0¨ý$|}¢×Ð^¢¢5ìêCã»?
þ??.9±ýÙàÚäÚE¯Z+WqåWÑü÷y8ÆpSÍû\qÄ]àõuwtà#.K¼)çÙô"&,Ç`æÌÚ(¸ÝEìÜN®¼²Ò¦.³%#5{on¢Ã¤LÂ1øeXoÔz§ÐtLblÇ@~qróCsP!°pù}þT̨D7Ü¿¹ÐÜÉ#JIC!¡ømÛá£Ãñ@}÷qÃÌÝíåèd×|U*ucùMÃÛv¸ÈB0ùrd_x|`!¬ÛÉ*\¸éôßqÿ&¦üç¨4%!û³ã¿¨Óõ2ùNEG\íËsÝ7)Åà!I$è
.H´#)}BÎ#.Ih~cºX}à×Ö7Þyô#.ÖÄ>A¤Ò$Ìq%¸KæIÜ°Cyö¼#.¥Ùª
Ù÷ÒTÐ6¾Ê#áô?,C·päÌÿà?ü}]ºø{¿.á20ËÊÝǤ#.ÆKí${5">'<sÀûzrÌH»ÓVï¡ÛÌy²©éTkZ ìoHiú°DpDØêø>có÷ê`éCtýMè'çKųÚ[¸g A£Æ°"aIeaý¾!AüooGÈë?Gr{ÓüLÎ5A¿gÔÞüâµê a®ý°GÌÁØ]w>3!´õ"zIt{#)î}Á·¸®C|Aʺ2Õ±óäÍ`Ï1s¤®íØÀT 4ñ\3¡'_¸Èè½-
ì¦ÍAh<©¢#/(b¨¡ä/NFBæ=²{_ÀÁ«ß£Î±<Ï»õò;ï#)8°gà>Í1#$>/|}?!à¡?¤Dµí=ÔÞTµ~Pp÷¤Èú=/é7ZUYßfi&;C|2o·g¥0Þ~G?U§©¬â½goP~cÄ_N´]#/9ü¾ÁvTO©C¿ú5Í[ðTäé#)1Ó÷ý#)ß óq#)nanrð¥¼gxòñõë}Ô.5õµ®É99gòtAmEèè©Ì#)põö`ÃXL3TþéÒyNÿÈ53ÞúìWùÔ}gj1ÑO×d$Ô#.¤!»]H¬j¨3;q«Èkø¿:Mõã$ÕXF%²Ân@¤é#"ô¯NcÁþkLÂÉÍq4©TN9ã#.QC +===ò fzAEQ1!®qãÊñ#&Ë9«#.gt æSÀ×¼9O7íèe `Näoá=7ÝÙû¼¤(Ï«¹ñ=,`ÂD¡ÀýiVÕ.ÕVU]ms]ÍÐèný/°ên@q*ÍFàlÍ3v*´#)õAéèdzö§yì`x£ÜN9z|í}mùêîÖΫå!#)òb3&¤¾k?²Oó0%Sþ[»O÷î]X©Æ, ÃÍ8Ù©LÑåy/WiOSPÏÌ8¬M0·^Íë¤ÞÕÏø7 ²Ã¼¨"Ã3éåâ³MÞDÇ?Ü1wk`ÍaÉ5ÝÉÀc´Äuõ_ä:Î¥O=EM#.DPâÙ+x§æcóîÁ:¶OAèÅ<ÇåÍ[H̬B4XXAÉ·FÉ:_í{n«ÞQù fâ4&sblÈ<ù#ÈõÈÍV3ëCM¹1úlÙÈ1äü#.f1AØrÙis¤8ãæPDfvèÂAA1 4&õ£a¦Ö,WZ´´|ÜF.=CúHcÐvqüqòÐlÀ/2Z8²éd+l±´ Jûäy¢D
GiBN!ÐA8ô?(À< ±D´æ¶'®öEbhj<¦ÿ¯T*c`tKv*âBõ1¥<1ÊCðøè§òùln\(*wä#)á&|ÿo£Y§§ãÅÑØ
=¡à¢&îÌãp¢©K¡z4d>Û8×LEdÊfc/yWÃ×k;µr׸ګÑ*fÕ62L¤i¤Øÿ#)æM¤:}]~ºåÉîß( #.H$Óê¬;Û°éô PdçÔÄÈCõh~c·¥±EZ VÙì=Õ³käÁô:¯CuÃ0¢R$#)ä±UO¶l#.$1½CsÉ"Õ3¹¤àm~ìF\ôÇU6Õ¤fÅd þT\^!à=c<];7£±þôaY¾Hn#/#.«Kk`¸Äjm#)E¤Üð#.´ÍWXûå¥Â{3i¸Ø:¡[$Øî½þ\ÂyùÙ@)\¯´Ö8FäòD¾Yµh£5TÕHBªÇìÿÚæql^ÙÌãuÃAöÎpÆÅÏõðiùú¾IVaèt#.A¡#.¦a$#$Z6ÙEîÏyïü Ðjo½F§O3!une0(ÆKÂ*QA¡ ÚºnP2'wé#.ƨ릡3¯÷Cµ'!L¤C¦ù"$?0(òÇoÏùIúv&"/SÓäçèL@dòãàpe²=aQ¿äþä¯Ë»ûbëÇaÅ=®îO§Pg}ÑÕ4öÚI"\LÉ9¶Ä}ÈRkÖ{ãð6ûBU0Oä`oßñÏèÈhÈ(Ø©ü?~vaÉþ#çÀ3:y]âUÅÞÈQ0ò;pëN@,Í8?àý<ß(o¨Ã?<=ííônkDa%Æ a#2JÅö²%jJÍÈiË¥P¿©ú¡-öÖðôAò=?xaì=cí=G#GÞx#/¸*bÂÒ'»ø°Ó´7v=õ61æâõ^Cû8;òÇgÚ'hB¾GbAú]ÍÀIGB¯¯óh6RMÄ«K#.Ï°È1¤:î¬mLͤ5íÁ(VéHP,!0ÕrC$p<x:,õÙRx`çaËÖ¥ ]x·¬ñºÄQV3&îlxyùtºÃ¿âè¥èÔï#/#/å$»òÐÆ#):dØöp>î[èP²ÉY#.ðrzÔÏjZ¤©GZV
úpï£#/b¼^¿;HÁ£ltwD³e¤Qøþxª¡w¶A! tE¦5s~xuNm£YÌ×úâ?½l²}pîÚÂ"ûçM\Ï CÊS¦©v¦Îk%eQðöîËóöÕèÑæ:/ ûz½Ù2A^-QÈ<#)úv[]z&áCÖ{_G{Ô«ð zßxóTÅ/סåSýÍt¥W= rÂÈ2°{Àì^_¿Ómwjâ><U(ôä¾k°â±Æ¢D¸³iÔC°Ã$ãâ}]@rlÆmÈ#.ÀùðÌ33@¤ªÉtÉaVd9OWC§lóÜñAhã°6 ù¦-)lÃ0c¶1ÍVÞ¾äN1Úy'÷¬_r³ñ#.ÌîûMLz5\»ðH/·c ¼>F=þ¡oêèåÂgòÖ3Q[À¥Ý¿Ô¨fzån,ÄWغ«?¶W òÕ#)ö~Ûèêò±³^Âzø&æV|ýSó©P÷ýúÜÁ¦¬g4_ìûò~Hï)Øç}&Í_¼U=rþk/8ê¥}åS¤ÇÄ-ÎÔ[³v@&)¨®Zô1קçS
®kªÈ¿¨Àïs@¸,cdÑ\T@BÓ§nt~Í°ÇÖ#.L#¨3.èb~é«=ÎãÈcHh<Ï E1?$ùÒ=Glþhd 3 Åõ#.#)ôP÷/ô?8ø\E¡´:}W°õÞ#/x?é¼²|záï&çdÓÙ=5ìØBC3þ~§¼ Ä¿åEo< }¾AQ!e·%Ðìç;&`N¤È{ h4Eø}_#ÙôÖ¶ØE¾¡¦&6ö¦([¼Õ·Ü½W³zúAWi;'p>j^rä!AæóaL7-íci«þTY8"éÏ97Û.ùÆWÕîàü=§üì§éÖS
sOçËMl p|0IÑË îWbV ¨!¢h=±{¨(òÅQñHæaÛ_oÖs âfê³U÷}p2¶nÐX8² D~DÐ@ôûPôDGÈüó¼Z#)]äBek6Þè#)Ä/<k¼9öô9Õìè=LgÃnÑèz¡:ØF×ýï>ÿôwEæUØ~4$êFvbí¬ÁMÓµ
dD÷ýîÍ Ï4Īðg4ÄoÀ¹[»þM«lOoþò#))0]5ß÷êC+Pf#.£NfEPïm8É'V&âÊÿ%&bEa Ö)eÔ3jÓ©ÝÝßp&c»æãæ&'ÔÐî®W[#ó©Áå[*¼jÈqd6'9yÚ_γ1óç$¿ Òc¿ÃõµE<*@±0~Àß>¯4ò~Ä8C'nóy÷вòE2=m½h8ÌmÙ÷[¡Ihy~M³ÔÃ#)_#~Ïeô?§Zû[«ü}QÙÌT¡÷ÏGÐ>%ËÛ£INØáääÀ>è1ÙÅìÖé³AähÅìö4ÃE"I¢ò0Òí¾ ôQþçþ8èè-bÍ^ÜÅ® 5`Öóßï÷ïpÈGï¥Ü5ð¶5Øò1>gÌRe¡0!#)Æqa°aèÖ·¾RNÛSjÐ{=õÉjiûÞëæïRgþñ@Uʺ?#/¯¢±ÚiþÇïíC§ôJa0!3£,¬&ab#.f´ Q@hÑdÞ)_é$7ØÃsLQ0àJjD^JĤh+1T¨Äi´PdAF1RÄ¢S4йô 5éÌ4ÿ±j3å#.Ü+ÿ¤$-ßzxÙº%)A~ø~nñÛvt
þ=ªJÐ1+~k;k#.0ÅÞÝÀ8$0Ü2I1â#.ïº(äZ9&¦Tê4ÄrqUDÁÂM|ä«(;7Æ;/¦ãGÐ8~6åä%-@Ó8¡Ð"ëúpúó7íýØþÅÈQ×æð¡ç&{§l%ÃL××ý<gI]ÿ-éF¿wèñtc;±°øЪ1QÁZoæ!(¦¢´%°4äÔìíì²vÉ[û?jBO\ÊÒLÎü9ÍT¡¨| ]!G"#)*¤vPüVN÷«4e*v±EÈåÚ¡¡ÄÎcÈÅGåÏ¥-+×óáBlEÐÊÒ©PÏßùú&¦ò~ï<òy÷o[D32øÊ:ørT2D°¾}tþd@.ARDsüûl,ZÌéñ7·ÏThdy¤9Xüü¥aG¡ìDù¸ ±¹Z3iÛ¾¿ò¹Â[nV¥ViÄPb4è²b¨ÐÚ0Çîv`â)Vr `÷lv¿¼túWHüC$ÝÓ _LteÃc¹% +Õ×I##mï#.^N#C«ïZAL]§\TòrøéôÉË*fÐ| ¤*é¤EêBÑ,¤7Bâ#é8ÁMoPüämÞ§BÆPl\`\·úÐÖ<Ö¼ ê&iZr úKÙNOðP¿Ó4×ôîÿ4sÞùrߦ!ß\#åöyd©ÚçÛ1ã8òE>fñ¾Æ|´ÃdkûN[×|Ù¶éÍ.¡2Ú#fwGËæ·Ë\ôKضA\ócSä¥ÿOð+h×êf!¦nÍñc«Õýjÿ_-î9>S6#RNç`²,6µÏÂY)jöK`Ãg,O(j+'BB£Í¬Ø&çmþÆ+áÍÖ¨[0ítn¸ïÚFZj?.&õgy±´2i«Ûîª)ѱ¶«m#.h,×8ª®±2+òL²~¨!9øÐË·úõ_ ÿo8Kâæ«£Lé¥û#)Eç9±[1¹ãÐØòÖ¼ÊôÊÃIïíð>Y#Í80Ü_Īk᤾QoÕnîR¢>{>¨Ã¦¼È,Ì·)}¥ù¼ðy?ëÇ`¬xéH5ÕY2¿Øé®ïI¢ÇÜíët¨ÔgRæºÏmÖ]¶×ÏçË]Ö$ªiJÓ`¹¹e*¢éÙ{_á/8k¶R´¶ÃeÙ ÊoÚ/Go3£öçZÊ5ÇLwƱçãwÇI2[i®»¸èâe¡¯;¯#.7£2õRÁ$ÆSÝæýÏ=N4köe´©fÝzÔ!A¢Ò ?%5Æû"I¥M¼Â2WcÓð§=5ZѪ¹lI«´Æ!6Jù6Äa1ÌÓäû£Vr%N/yyS^YÞîq/!0;dC»«æepÕt9s
RG1ëØÆÞyÈgX5<ÇûPÁë9<¿8¼§¯A$!LÓR¢¦hÐjRTÔ}÷KO4ãÓÇ7Uî®\b¦½K ®}ïÚ¡£ó¸Ï.KfáÃB³Fáæµ×wù8X5L§9ú?YèùÀ¿§å³ç¿J4"óO[È|%Ͻâ>ø¦ÐÝ5»ÕÓ;å\ô8Ù/¾î¼í¾ü$øÕf¸ZEç7¿'mͦîÇm6ôoôÅiõ;ãSùtr²îÇ#/ +ü½;Cë&³÷syºãÌ`X©UöcdÛó»Þo¨Õ~aÖ;Y)z\!%! ì\¬ÍÀ1ó»S_Õð°1ÛÇ4PøÖ'&RÄ?c&6®ä2Éé\ÒùW^¾Õ×^iÁ8²ië`h1lH(ñøQ}Ã?×Àÿù¯pûîûïºxÇ 9H¸Çºí¼ØÙ¨°èå<5»ôCúJÞ¦'ê¯k³0j:^@K£öAÖR²@ÍjÜ`ûÇ,béUOàïdÇf;ðÃ$YÑöÎ|Õ
-ÅúqéM¶ÒAãá!Þ_<YÞlÒ#{h9øL1<·è]¡l#©Q]+2FS'È1XMÇM9Ä3¥ËyQ¼Ï´ùèì
Ä dáɤÓFzÐ6,HÀG#ß@S °ì6tfJ©Öï§ù¨ ®#.b´)u-Bív4ò* ȹWPåÄ;H\qÕ9ÁÇÈù`ÃM#)ØÁb3 - áíe|ó~ntþFX²d4·ìüÍæý3-½7iÀÔ8r9KE²jó9ßoËÓ;DÇ7#/ îrtlÎÉK(gWÊôÄvvGá@ãF³ùoù/¦üE¼w0Ó_R¨ü&Kóõ9#«¦ýÑ]xµ ÊÁÙùv±/í¼ÿ«DÆu¡aÆå¿õã¢jYm?W³ì1²²´`ú¿³ë&¦ýýñ©AÀQD3¯ï³|Ïßø¾¯Õùi0ßAɺüj_1úÝâ¶ýÁ&bdÛñ AÃ^à+Zæ/LhE¸ Vj? ^6±(èzù?ÞÃg}Îã´Ä+&F"qòlÏÓi!²#.ö¬ÑÇk¨!àØ=eG]0`nÈh`ßnf¼yQõHx@ oÞ¬"ó8ض¦|É[Op0Ô#.áÔÅø^1ïÁþÓCP##.ŶhoÜtõâÔ£Ö*)dPVà! lYÐäwÿ¸lUUyÕ7$SÄ8aÌ^§<®@Àqíóö2,Í,^ÆgÚ}Zý°Ñí?HCBv`$KZ Úñ<¨ééh¦³Ú÷yâ°NÒGÛ»úÇËçßVñ
föE¶¾Ã>QNê5SfXñþ:?®;¤i0F#u²°ÃAR#)ÃÆ#.@ ZTÈrh ^É1ÙE3¶q2#)àúÓ#.Á§\ì.þis.¸âØaµ`5«±ë6Ìa%( ÐEñ]ín;6JMö£½¹¹õ+×;%Ç×ùçÓÃ\}Ûü^ÚÖ_/nooß:OݪÍuÎ_]l¼}·MJï3Øî¢hüçcz¶rW¤æc¨8dÍÒþiu±¸j£#.õ£RFádA ò¤f¿PÃJñùPIHÅU!#/êýeüVõ3YK¥úÌJ0`Neæa¡1>ï¼üíeTUYjÖyܳùa ªú·2£3HtQ¦Åߣ¾XïßD;Gúçgag uT%¬&¥¡}¿ÊÜ;-®§@éP6ÄLXtY ªöb¶Õ/^)#/y4>¾æB½68FÒr#.5¨4Îbq©%MÈaÿ~¸ÀYM²sx!QYÆ;wÖ&ǼL3{s#)"ÈI¢?q£ì8Ði¦#.?O¾I°÷ý{$qϯieìË()+Û«mðÏÀáq¶Ò+pA1h=¼ðH`x£»Mùg¦ðòè)ºÇþ¿Ñ&Is:ûXÎ?$ÝD0IüCÄ#.!Ì´É´±#^LÓV¥#.¯ÓÝî[6õìx<Ux#.*¡M#/±µ±qSndk1°@¤^[_#/Òwqù7IÛ%¯vPv>¨î²(ãÆtiM:t¯:3&7vÞ;V<$qÅàHñë\]G)ËgMÌÎDbuLßæ6á"ÜÑ)²½8غ±0u"q]Ë÷NàÝìÞC§b1BÓ5kmþÙæxÌľ밨} f£l`â!´j#)có¨·ww}¥Zð,qmf5b#/LÌÀµÈìËE[ø×}¸ã9;dÕCdÚª¨_.¼{¼9:né·Ó^÷7L4ÐijrW/OÔ6oÅÄìÎê{®R &TƹÍiÔÙPHÝ;åÄpM/£¾zè·#Ûíägóöz"T$5ÜíÐéÑPÓì§w+#½#/1hJ£h}û£Pà»És}r"lh@â
¡áÐÚùÈéÒæÔ!31¼´´0°Ô·èë¹s2P^m#.PÃ=¢ÃSF¤Ëz·¨sëÊIô&¸JâÆ9MøïtbP^#/¦$9^Ñâ1/غã»K2÷DÌ2ØóC j)®ÐÜ} t×Æ·ü`¸w¤S0
³ØçÁºm¹¢ÂÜcÂ2Û:KEJìF bhnÍê¼ùûS^ÓZv¼'/N8jöæ9ÂÅ9>ñòÛ}°ðò³mÅ%J#.¼ÜqóεªiSnS-.·HÙ KÄ=O³´dÔ¡Ô§óJÒ%è<¢âYÕêr^£#"0Új;8¯d{NæCðÏMZ=F¨ê4çlt'&¢Ê#/rù<mç+»Ëç>vòcv #bfF|ý¹©%#..f\y.ffeÌÌXÌÆècÌËà-Í>yµS=ÙÒ#/@ânú2ç!1¬X !ßT ^o#/ºdÑWÀïǨxS<äLr9Éåì?²âãßåÔ
½y×>{ä]:é,F`ÑX¢¯Y8ò¥qX~*ºu¶pºó¾"µlÝáÏDΰL.Ó:ÐØjebÆ*Ya2ALät[Ãçcã¥g»L¸»K3ù4AP;o#/U3DÇm½¾Êë»2qÝ(.Î ÅØ.b[Ïá·VgaØ¿#.Òñµé!ÇFjFlf XÀÚ-ê´µâKÜ#/Î
)·Ï¶ùUFÑT±¦XmRðN&ðÚwC.¨¦³²Ä¿óÉ PN³¬åª**ò,gR¼å±qª &â¬$U»ûe¼´FÁÒà×#.!c°èLK
9bcÃ1!=üþ.ôÀ{7~¼òOïX4OoªqX9lÏ4|K3î'áø·« 6ÇÜ m£¦³è#)¹Iáãzc¨iÒºtvðQy/%ò]ØFê4sÒÇË#/eOtÕñD=¦å!48(ÒEtTêѺ+gqØêuÒÙ)ZáFRg?Ûûæ¯AÄEPãXN=÷Xté"¦#/Ý`îU3´0h/ð&N§WLr,d#.qéCCiC'xCi1AÂQÖ.Åøf®é,9PFô2c`ÒÆg·¸-b*þ}#/ ³,YHɹ#)ÆMÇtóN¡ØÎí8ç5u)Í2ØÆó>ß^ÌôƲÚbÍgá`Öð)Ï°Gx£#ôÐÑcÚ1@5ÆÚâÆÆ2dLQ+"©h@ÕÚ:Ül°ÁrÉMVÎ2¢x£$ØîÁXm¹¹MR,Èftö¶ð¸¦©D½ÍM{,k>o@v+r¤S¶Irû²`dSp7iï1â;j]ó;[1õÆÐ;?XïÔfZgPd#)Û>F¡ìÃ9KÐb'Àc÷À[o°wrÄ`²5y¯áòØÆ¡Ï(:_ШR<Î8âFùÉdmº{U¶ÛUV¿?qÙË¡e#Û:öùÓtg Êy3D]v$¹íöbÛN'V1Õ¶Çxy-ÛÚ¯xpi2.ÚÀ1=ÁPZo±oAAap¸%WEÐo÷â4;SÉ3ÐÉ,Ðð@öq5¯->|#/ôó*JvI¢BÙU÷k6ÜÆäÓ?qBhÈ°ä¹Úí*#/éÏÒhé§o®´
Ò·3ÇëÔXº*ÑÔDÈ2ü@º´ÕlÉk£WeÛ#ù#÷ ´®Á ðàÚï}+¥xöfÜðOùd1©ZZùÿó±µyu#)Å©L8WxÚgvmÜ ôI¾ÿ"8||ÌÃ#/&&"2 (ÓI&in!ãÔåQ23¯ýë¸ õÙ] + ¸
J@ør<ßÞ_,AÑäå( L?½äú/ó!àÿ§þ´²Ñä#.Y"0ýùßwÁEosnHc0D ÿ->¯W¿ù')]_«àà{¬Çbäy1åæÆ|iaI¥jM¼îÖRl¥·Ïn¼9ÒpKº¡¾pFjÄQ¦?Ùïk×5´[D¸~Rúì³·³8;CTjÀ]øí{4©ü Oí©6eÁÉ#)pQO.¬#)âxkç_,<4â¾×£6öóÂ'¡ÚóÙ+H{Åüþ,A;8C¨_#.Ñû¸ò"Vô|Ê E@êSÐèù 'ÕbA #)öóª¹þ-E#)ÉLËærÒ¥$KZ_y°Ì[1Í^û¶ñZ"]»iò8ÃÉÀgcÈéD$ÁnÞFHä
ãO&Xûsdï3ªÆhÇ/?°Ä5 $¯x«Àð,ôcðàãÌñÃQÁׯhúè<¨*®Ø#)´L¸{Y¸ÒÌe7Ãò#)7¹ÝÝòBösÛ²àöH@Ð$C #)ü#.ìíDª¿ßAAXGx*À£Üpû`Owz/Âùß;ê`K\ë
Dm¥©DP)ö²¶×ÄëP5°¦1#.ÂAØô#)¯NåÊôúzôèÝÚõ"ñ¼4¢Mõ×o¹z©skéwW+ÙÞy¯;®ºãJÍÝbå>3¯3"¬8#)Ä#.<J&-x8<Bjø÷ãß)3$l(&&¼´¡ô}#)ÆÂȧ×nÂqÿ#. êñcþTU¨ôÂ!=-lÿ.C³ë\(¦ÛNHx%#.2´§hðÏb1-0?9%°Õ8b-]#UI¥¸ú5èÕ¤éÙuÆYòÀ8î2¶íñ,#.ã¬0n¸Gp(Tãv lêfIß?w¼åx}|ûD×T=à`¯õ±÷XAêývaä%òÙ-ø*êÒ)ÀÊG£$6INM#)®¨"¤mX±´qëÀÇbz,b`W½E-©´¡Âó/+Ieæ¦ö#Z?"Mb¬8ý8%sS&ø»Ù¬ü¿llc°õt¦> £k#1ípÞØ[ë#.½%áÛñ8¿zôyø¥={ùzÓ,ô9 ñxÿYáôÈ·MD>í&L0j¤Ì¯vJêÅÅ^Hyüì~?wäA¦2ßOw`wy- èNX¾ÐÙbhÈQùÔÞ鸣@Øâ£ðTîïXãdó³(§´NÒ¯w_s«#/DÂäÿ#KªÅ×¹ùOÇKi¹.k#.43°¦xp¯ÉÝ´ýÐÐÌÓ$8îÊD{`»Ø^ÉÕ@ùJ
m¶6Ú1KTdT¢MlU#.µ#/m¦¶5e*X#/#/e
XE$D~rWuø®ý~HÊðî>ÐÈFiCÇæ¡á$ÁêõrÑ?#:hÙF
¬a¤FüoGR£[Ø5¾Ï-6ÈËðõ¨uE±^¨$#.à$Ò257Ðk©²bzf¦ãe¨øXÀ0xÄpÆ8ò
Xjhi)îäº\_p±Æàzó~@IÅWåù»j¢«Ô!"¿(S©Xv3ÎÒ0s¥Z"ÚTºpb!døaGßôGÌa0 !¯ÀFöâ c!7]ìf ´hÅfUôg5#)ä¹$AL´%*{!2JT5'Næ}öm¥㧹ÅC±öEa¨B«ì^jx·Stl"þ£lã7gg0PþdÚrÆ36¿?3B}oÃ>¹_fH
?z¡÷tBùi¿éÃg»Ñöý½Ýý÷\çC+DHfâ"$
÷yàÛÀÊL°¦Ö+dÀxNH§Ø#/ÉøÞjHa1(dÚ¢n{ù]_3Ìïïí³YÁo62ç)BþñEÓõr´?þïõhüZÄóÀ Å;x«mÝÕÙ2è>±¾êÒú~zÕû±Që`ãuÂ(îgÛæ5 -$´õú_¨Já pÂ.g46þfvÇkÂФf@zÊXj(¦N ɼÖ^N]ó²ÿ_»ó£È`qæTY²ui"wi;K#.¨ß~ÂßÏJOòI#.ê#/ü¶×ÔvâÔA¿«a[HÐÝ.6¸k'Û3©àV#/|>/¼ôûds%¼8B`þHoL'´ùÓ~%cxLÑðæ¢Zà(i©³O9''M0jë`fþr)ÓÏæ%¤8¡á)HuÀû5Ö+ÔØS/¡âÕ 1¬QµYâý#.¹o<òk{DrAg/)HB1¦\áëðÐR? þ»®9ç3Ø
bMÛ¢sUkImD*QÃ7Bf¨§nðYìHù´Õb_°ÄÉPø¾ÀöaøÕCÔýI¶¡õ ù°oujB~>ßýå=raû#gø¿#.n®ÃQÉ¥Li:þ?³·¸ôçî("Z<0Ä"V! ¥óü|wAS¡ÂX:q@¾íct´ç¥Lp¥NÐ ¥ÐÌK0Z =õí¢§*p7x;âÔ¿x?pÅUó- ÆC²/i»#/ÚÝýÏðë È8.ú"dÛÛ¹ìW®ë¦+ä"PrËád÷xèhÞÚ#/ÑÁúôiÆöÒøWe7ÉèrKæÓɳ¬´¾&B´#PrAãßÞ$¬»{ù\q2q<Ô¡0ZØÓÊD±d´&ø¦àÓ;*^ù5ÙÆJý:és°Ö·Ô<_Fð7ÞB 6wSMWã~K5[ãv%oC¾
:gk#,´avéâ¬Va¢´A#.2Ü5M°¦WÏ<G¶» 4jÐâû¶ÏîçZnëtÆuof~\L%ÃÖXK9^ÊÞ¸®UpׯzñÒ¬ÑXèHØÌLÎÑÞ#ÖübVÓaºSÏW*àb<²|~8Í/Ïîû!¯åæcdjÖwn®ó;v·«mLXÆ«¼[Øv®#°ë;yíñ+2÷«#.ÑqsEÕ&µ[~>ËÉmש¶;ºZºlBy®\·$Å#)çf÷óÙ¢Ü +¬òÙåé« =£°ð¹p<áyllÛ³Ñqb~æ·¼0ä\×>ñµÆ@£á¤àW¦ËPd$¸úp׿+$àg6LË`×YGT8ÎýÏ?oÙðO3,;4ÈànÝ[«`=/s'äÒòKtF(,Íí ^îØ!~ÌÝõhkSMZ.B²GT8HÙSfF±ðá¦øf$#/@íV:L17$K8Læõ©ÝÖ5:&&$Ü;ûý+×Ë`°Îe}õâbjJPVÛï÷}tõú+{6ß!" Á0Á1êzÐ#.|sî{+71CÂ0Qßy0É!°³Ôɱ?×O ¡ÆÍc#.×fhÁh;m.#.ë´ÛWI]U5ôçîR2SR´ê¹ ÷ª¬à¿HƽÓn|uGI.ü#K·ÎM1ï^ý96gußJõQÞÐ7Õøû%`³tÙy4Èd3r6 K_n׳3-aÙ·ü¢QzCt£¨`V×0ZI AÌide)65t×dî·Rd¢,ÀC3<¡²pÌ0MNNHVBj0Ö-!]¾cqwLv4¼=®ZKTsàxj¢¤#/VQt'dzv ©¢(&B@À$Ør¥ùÖ ù$ÒÐÅ$ó×cËÃð:ñèÅð¯»Ës6ç88Û Y=ÒÊeeWÕ]v>ÇtNfzõSSÄÁõíA(õÇgì7=x}ûÍ^Rÿ )E/pÃ@¨nï' ]_ªÐ*ÝÙaÍh¤cÍCF!nÅ»~Õb³~úI_8©RnSB ½3| ³[Fq'ÅM´-:*Ó^wÞħ߹æ§$2*YÇ*å»rÝnÕÚÝÖò)yqw&61ÅYUÐèÓõìçB×¼aó ;æ#)¾!SÚw!ICa"P>mìyå]BÑ.A#/Ä#ó£põ¬Ë»*ú¯Àíñ}xª°¶ìú)ûª¯^Ç5+Ù¥yè? ÆϪb Wyfåyð%yd¤L6£k#)mdÃ0n`øHÌ}»5㬵ÛxãQû½úWHT û³o Fqµ# ÂH1R;#.4Óu0HL7±ý¶°R¤Ù|g(z|zy··&<©pØF»UÎýg8¨Ì·èM½z㥵E2h¡P=GG$hÏ1÷5I®Pç²]oÇ× ZOß#/¥%þ̨I'9¸¦!_SØ{{róû%q Ý#/d)#/>Rñ8qÏW&|@ùó9w.p.NÓÙ÷r#GF qÛ7 Û¾1÷èþ3Ϫ{ÇCõpÀC^*ªÚî¬8ÌV£#)Vy¶ÒR¸h)6pÙEëÂ4Âji4+ºl ]ÂRbÞ!ù è:#/,ü;Ë5rnP@Û(¢NIB<¤ dÅåÎ÷e+ögÆ3ØÜtÛíÇF{S)|ê¦Bà<ÆqT $.¡¬K!L!#)È$#/ãl1Ù|ÌYL;µQ²VUFYH#/7àqSWIСen6\$Ľ¡¤)*Æ«x¥hcemðÖL#/xÎ-CÛ]»µééÛ^/ É>¨>Ü÷4_áqè6Ù'×Áv8óôQ||ÙQÓΡiva-á}§FÚÌNJC&`ÄFËÌû,gÒzËd@Ø©T1
1z»PIó0vzjáétQEvH[aÄäpÔ=ë4,ñF(&¸g"Vè<'6h KAHíãÇÕ¹ë¦ÍÇ6ÚVd6p³Àâ9¬ê@vP¨ d?Ndë´ÞNe²s( U¢IKÒøúÏìHJJ jyO2ôø~n¹Óba_¤<Ó¨lÛÉÛDÌÌêàWhÚÜåîT§Õpg&¨)F""(´]ºÝ¨bÕ`&(~4`pS#.ÃÓQÆçëÍ(,Ä{¤Ð3 Px9ÉÞÁý×F\"°qcÄJqS\%Å`ÈDÀhT#/=bQ©2ÈÞyESdq³[ùnÐ 1:yÝç¯Di%ì #)ÂÇ$úðMâ,TfrOÛÎ`Ú0öïpÒ u½¦ÿZßt4ĸ0º©@øÝuçV|ÖéÑ'|W¸ØA %<çwv¤ÃcvZÃ9
"§½[&úRÍÝc½}#Å·ÀÜ`³§·MØç-'Dm¢242àB0æË,¢C¸')ûOCåx°=BL2 îcúüAðhõOVNmÙ#/v`Òlu¨jG,ºdÔ$ÕèS¡.6ÎýbÆhÔ0eV"Pû³ÍÑi7Õ@j{¦ÒåRAqväiÇj¯DBQé#/°¬ædd#.²Öbe¥h¥(`Fiié=iê`*êÊ*¢XôUٴа<RQÖó>ºçq&èÿÉõ#ÃoݬO´oäÝ](ÜL½dÈ¥7GðJsqÇêm!¸¿<©Ph+wb;Æc+gWtF¶ïtpï®ÿk?ÌÌcqÖ#.bÃ~u#)à `'@d ¦É 1 ï$g±Êá#.Â4¢ 6ÍlXW &C¡d©·7@C³
»gDPPA±#ÎBè-#.`µ°©";FÛ/BkLFØÌDZcð8ÓThfáÈ4Ú5ìJ!8Á½®X®HPpLn;`ëéî%Í?§_©;wÆqëÌÄt\b#Tfè!UÅF`Å¥1xäÔJ®F»CSj$þ`¤4Âé¡ï698Ǥ³:-èpÀKz{ËÁÄ(Ç\l³·ªa²È¸:tôÒs,1ßÌ·|0Qó&=r!¨®'°ÐNRwèM¹çÊTê;°vë:s #.
ÅUVÉ@cÐã\ÚºêJoãÆÜ;¡ªNaFn]ZLb]7(áhM LÛcLYö2#.þÈm²ÈãǾ<±V}%ØOvÁÝ¿dæ»a¦6 4øRÌ.¸tªl!ñ8g8Çp&À8¿ÚæÈ)hÜëøs6ÃÞ&Ñ]¤ ³2L2XgBà#.Ïyd[qÌã H@À|CHðZ#)ÄÏÑì
Z÷¨ªt6a¥.Ë8¨ò6f{ffY²o&àÀ®M½Üö#)ù;t]ä\XA@hÇ=²dßB0«5 ,RIRl#¹¸ î/_HØ8àA%`=t$7X40uRÎÆÕ#)îú9)º¶ç_Àh) È%õWݼßÃOäm5G»fI¼Çò÷Ss$ÿ)ì.ï#.´V¥sÚáX¼Õøºãì'r@F «Ñ ±Â÷`zTÕS4-_SxÚñ&Q5¢¶Â¥¤r#)JU"EZT¤¬%Ô¢êJAÂ01zÏ¿ówªð°Cç_áRM«CØ$²·D°otóYÛ¡#)ß(@©¶|¼a¥NãÚûWܾ}&Ûo¯%!F ÉA¦ÔdÌRmI%m(d6T!%4F`IQ³$kæíÓ%i4lÒ«,Ó(ÉFZM¾=Ú ±³B$RI,¢6£$ Ôh%%)LQ¡1VR¶Be4FÍFbC(4$l,EËÄì@×nt:îhzÁÙ!5ÑTÁ2O
ÚN»}¾ÚèV#ü#/ãÑÞ²F¡J&K¯ÄåYÁRC2ºÖ³2¦ºO¯ ªY4czKë'sLI1eF{Cv!&ÒÎ:câMÒÝÌÃa9G#)n¢¢lËEÂÑËm/¶¨kG*$k¹ññôÒÍ£IpS?Gêû9çú*&;%M m°Ç*1½#)l#)àxt_Á_=SR±Ò[4ÒXѦZ&ja$LLÔÞëÚú;áôîøÜLÆ(Òq8ù-ñî5ê58·ZèzøiiðÑeàL>ݹÌ1&¤>{|0¶¢¦-'QÀÒڼà Lgà#.k¸êØÇ¥°f_rÑÍpⱩܢj{gÈ4kû,ÀG7îÁ{="@hû¶~K:|9:þ#/uQaBI!¿1èí?çùiÉ[þG^ËmqzbóÇcÐÉà©r×®Ì'iGwÅ¡_TÇ{Õ[ѹu°èÓ&lé6½Ì3ÁÂ׿8]¿ÍhôGU´â<6ìÿ^&>·¹ö 5Åûiï¦ýyå½zK¬ÇCÉn¶ÃxÞàæ#.OÚôk2`ºa&P/º`çî|n×g§Kùè»âCÈÃá3ÏNa¹j{Ív\y%Ù òI7"m^Fûe5uvvZOµÈï;¡Ç¥·zÍ-ª`@èsÜ÷ÖN#ÂHK½O)AÌ£½N«M²ènÏ×2R7;¯v@\ÃslêLfe3E,~{òspÔ3i.v0Ñ[E`Ç[õo2#.`÷õ0Ã@Üà²äû=C+¬!©LZô,g`öÀÍÖzp<Øõ¹ìÒãy_à3bh¨#.£°ª§~wiSjý
/ÓÇÒþM3r«a%&P¤ù4lòÆFN¶ãݹ¢-¿ÛÉ#)Á'0cgi¢75ÜiÞ3älz#ähjt'n:¢¾¾#.)'C¤
lå_ß#.Üø\@oíôìBLÝîñ#/Ý¥(:O ÑÁpBV6¨íh¥0|Ö¤û2;ÅãXMñbÞhlÉUGÑ{Vz¹Æk#RÜã®Â¡º^_ªf\ÍÌ5/2åÐóL£BÒÅ.K¯ËR½)Lf{fñì¦,ëÓ3Dt
.þIzM!8y/[µ,33ËL,¾Üêâÿ8Põru/
ÍNXΣ92L3Hà])éjã<¢Q¹¤ÿÄh Öյ˿32b]eØé/5Q
Æ3dîv]SaRfsWLfKÚV÷-Ö*MÿlàËÂ9THó3o Ga¡ó¡áÙÍ'˼óÜ7nø2ÐŲê;Uq¢6§$jæ8ÝîuB <Íâ1WLÈWÞÔj2a2&¢ÔÉÝAÁ×ç3©¶qYÎÈ>.¯7ÍÚ?»ÕÛ²ÞÙ'81G^|ʾ}\ÚÔRd+ìäÖn×ÎÛÿ!öìy(¢)Ãï ÚÐp¥>$çfl¤Åü»Æ!bÞôN§8|ñ½Á¥·8:gW!ä/>7S¸xíÇ)Þns/x.ï>¦4tI&ÚaÔ:ÞÄq²fç¡7b§#LüT8»ð,ñWSyv:¹ÔÁÒûpBÎ{ó'%¶:<n GtÆïvèç£w^ÜL;¾Û·àwÙÁnÛA¼²{(ÆPïU)ѺlââÓ*tciã3j¥ \4Í(,dÄB,¡åijíqW¢ð¸ÅCîoq$,p
±3È5µª¶92D½pCgV,7Å*h(ô4ÕYi»â«PzjkÏænJ 3»Hhîõ¹«uÉ5uÒbôál¦!¬6#-¦|Gnû3S®Ñ[¨ùO,º¼jT4ã}ø}ìH\hfngW~OMÌæ¦/®èL¢çÞé°È.ãvVͼàÊ˾èuÐG4ç&ÍSËL1sº²Î,Ës,)Ëf6hHè;WPÐìO#/÷Ww-RVðéÓ{ÞlÖ ÇÙxPVI証pçHÜX¹¢@G°D&ÞÞy[Àªgv̦QAÃå¥Áa±·J^º4q¬å--Qi¦g*XÙ8#9S"ALòíP¹á#/°H§)úé¹yÂè*Ðí©§$$?÷[²lU«!¶û»Öçs7k?Nò\8|pbÙ9ú xñ«¨ÆBÂ3z~Ìb,¡Ó»¹s
?ÀOË7¤*ãn,rÏ®ím]j³£#.à L(ù×9àûfzdM®xx(áàùÉGh,¬Ta¨µa©aáazÀ»á^.²ëL¦7éÒI8ÉBn×á»GèB±Ø£îɨøöP>=/Û¾O>`únîæ°Ê[«;rö]n}Éá#âõ(Núví3x·HÄa¢ÑyÍÕ HK*2cV1ïÃh(¡ß|c aÑuL%¾dnE¦pÅÇ;©ºefXÛoÌ4êd ddºæQö2½Üvï%ÜÇMh®ÍÙ4Ìu¹kDÈJ6ÉFPw1fûomYah£3Z¶Ú¶²Ãú¨¡n-§@+DñnulÅ[ÒøñÚ6BlÛêð4Nñiiäa¤·D¦`Í=á
ÔçqÔRvÈÙ#Ú|ÕÉBᣨÌJÖîÛkÆD6BnË[LQ$Ô¹8Xj0]#.IªðLΨÔÂ-1FL"&ÞDª% ªtM8
bqLÏgö1y®!ѶæÕ·ÎyÉ0&A·Òm :\±¨B Ý:tfù6¸#.Ô@Áç`#wVHwξìoßÒkè#/<q#$WN ÅÞ "&$.ú#.5ÂrÑ{bðàÙFGX@põÂNbAÆ:ç:®â'< C29ñw 7R¾{ö3%w&b¶(ûX#a®×¦A¦Á²NÎeM2:°§#/Þ°áÐiÈÀX$È1anC
BÒaÏX
Ú¬lQ$#.M5#.´3UHlBÞII#.¬lÚMUÑÂJ`±MÕ<4¬¢lÝ-Y¢Ù³&P7fqG'jȦª8ììÔeÐÔtt:è?X0¤F¨]eKÁà]D7F!{µI©UNØ:äuuGç´p0ØàØÞÍ&Ó¡2jlkÖqÀt2&Â\!áò·¡k8ÐðÜBÃåò_ÅuHsCºi]wCÃÅ cÐdÒi´;¬FhiBàÙº5lÚiÕ¶yx¦dC2rHâhHHQq/´ÄÉf^q(äCøèµêP
Ml$H&ÓspÐYª®9(yê
âì>Î, È/(wÇ¿bü_NÁ:K`d¯)h"6$4Dý#¶Cº]û»NÅÆxð\¶ £¢;y[â÷dyËióJv!¶; ½Ú
Èqç[¨Ìt&#.dCQ#2:t61Hp¤à{¨¨7:rä´á÷"äMeïrb74!#.Qõ`½,]¡)TD:9GL¬më¬Ò½©{^A¾_aÚNKuÄFX`§IËD+¦RÙSã5:Q½g Á(&f9®zM4Î3#.¼ã ¨ÄMFáÎ(LZ¶Áa:¡ÇI3³dpjЮéVBØ»´w±8Ùc PÜ#. æ©¥¶26Æ)$sËãß¾Ûã<É6 /²Û\]CÄÏÖÒÃ)Xа:ÓÁ$gTXix`©D©õ°YÁ´(à½
©¨¡oõ¦"¦ÃÏûõ?°>ß?#)èTm}WUÍ3Z22#/6ÊÙÀü¯ônRô è0Aá(w 'aÌÙ%3?#.È ³DÀQÃg.êìi¨EÑ
®¥ý±{¾aðê\Q¦FO>8ÌƵ:85Îâr9ÕçûO+ú¾
ÏG®èv{©´"ʪ1nð¸lrN.8´Sv!Ôôvv]Ð6äÓM {Ø¿i`eV$<5èB(ts1uY¼¤ÊÒò0ßj`Í°¿t4%AÆbJg¸ý]x¨ì8á`×ñlC0D ;°D¨èæ ðøB`î"ù#.Ö^¡6ÇO¥7Ð÷qÄR¢ÕTcïQ jÓn}>À8)ÁbýÓðØMJ¯à½?_@å$û$Ô:5DdI64×vg7D±mÒýóSñ)³ Ê\õªJ¡â=¥÷ê]?Ì{® بuviü[#ÞðOyÜñ$îîÅì
ñ6Ú_´þDð'\Ì È>I¥a&ùIPÀÒaìj7© «ÝCý?O¡ðî«ðÔÔ$gaÓy®!Ñ+«'9ZÒÄRVÏ P¼| 7¢½fÌMvd6cê.ØFz# TïÐÚäî:3',Aâl»)º©>>?o¾)4·ÈÅÈè8ùµ/0û³éûá£-u&µ£3à}}ðyÉ%)+lm¥+FµTRÓ3Q&6ÑfWÆ÷ÛP"{èYÇCóúQ×O·«Íä¼@ÒN!<¸C÷r8yÉã¦~W{`¤ãS% ©T4iK}dæÐ 3RÔaì=37S¬ñÔZúݤÿbï¼À;¥=Ñ(ACôJA)W¹ªKêUHÜÖú»9i« ØV,2¬ëª ¬Ðë°ºpÒ*o*lBBrP%¬¤Rj¥·´#/Døíø F(Ð=7ÁÌÌT2HeüvÈi«9;ó`{ ᪷4Ö5©ØM¿óXMÄCVrú»u#LäØ#)²k*°ÙÕÏ,$¬RäkÅy7ÔW¯«ã@4Å·,ÙÛJIu1MÈA¶ÈÈBû¯aÄ7ÈÇÄ8Úã¢ñÉÚVÚS¡¨4îÓß¿èûÕït8{=æ;l Äê\Ñ:1ȾõÈ1b[1(¿iZÁVÒ«ôÒr qQAQyÃh?FIîÜ¿c7F¥_N·5T+¨IùX±¡0KÃ7Ò35oÖC Êlý£nß6éÓJ[imSÏClc]º-í¬n°62´6&$ih{kqÂÆZƱbò"gúôÔt3¤3ÎU3¦ãÓ#¾ï<f6]s0RÊ´<l<5æTwu8~Véú4Ó«ùÄÕ#QùöÉ£cÝeÜsmÃØáRä d;^C§:>#.EÂ"bèsµ#)ÐÎÈØ.BPsONIDn.¶Ê°8ËwbÂÊ íJ0ÙÐÎxô#.ÍÕäǸB¦#);`¦Âà@cò^FÀ!·4È@¢hì0l÷Ç`oÓsÃüè°¤ýLÜ(Iá×Ú^WÕª_äúõú:Û}Åv¨=GúÏ3ª?\°¨¸}Ç0á¢ýþà#/ܾ^yCgû 9GD#)H°þkúîÄ4CÍo"Á3ÌÌÈÌÔuý£B¥"%@£ï ìö¹úxIb·û§#/óúàZ¢§5ßê?»Æ1Hûsq»N++ñÜLµÙñÙhâ×A±UbW*9g]8c}IºÓj)%
«§|V)³ØL>SÜj¦MØF#.äÀ<4À`CÅLeN^ÓA¤AÆp1IMW0·î7ÆÓ©iD)zº¯·zè9²LSN±yâG#/ª6©#.0åØìÍaÅ!#FdËÔCD^e±j´§Qó¦¾0½¬9ÇG#.#.ýn@t~npË$OÝ<PmN8°Å)ón4ÛÛnµ®d©X3ªÓî"1&JðÇX$©ìAxJ3·Ó)N%÷d®'Î"¸I]5Ù@wM¶$z8ÉÁâ¸.#.g°ßìL«ñZï#)ÐÐ2ó³m ÞÙÏU£EÆXrfD8$§Í8üÍÖ¥aâÔnb£
2¤Ø´7[µÖj)bÚøW<Hà#/ÄHÒ7ËO{¶k^5ý-Mè=·jr(cHËkl]µw%Û®3YÎשιÓ;tÇ]ÆUå«¥A]jí%`Ü®ë·wi^wJ¢d,Kg#.RP#¸,ITºsóòऻ¸c©Öâ½v«xØÊlÓ)2&ÍlVjh¦k©jéZY¨I"eZH÷û»®ðßHÓ5£RÕ¤¨ÖÊH#/ð6ùüÎ0U¶Ä¸'$S#/çMΩ®ìÒht'yá©b#.P(1Å'l3iAºN'tI.¡Û]gÎÈvdnÎÁ$6#.¾Ù¤ìÞI<#.Ï.EPÕÅ_8¯ÅÂ#/C ¤Î^CíöæadÎfaY4}¶uI©6(ª+ÇM±Ç4ÅS´hØU#.ÙíJâc°ã{Â@ÀmLLLÃ[`;²¨#.I´JFæäo#K`¦EÓª;0¾/SqìÀq*fs mMXç0pßʤ#/)¤RÚ[ªi¤XoR£¢ó]& o5XïzóD!¡7DÇ'4Yn=ÜÊíµêÅÓ
Æ9fWhXòtÐÃ?>Oâ»?D¿øM+Öð4x{#.`0èÄ¢Ö0xÿ>å§v"ï*U4${Ú@Äá3ýAC¯âÚç|ðP»Tt02®#/Èdrp²@fÉÉ/ñ"aG©£©&èm<¤íüÓ¦ït±ÆÀ·]]Þ´?@?Ê£*ÈJ§³åøséú<Ý(ÚTìÃ{¶ÜyAOÝm¿*8{¿°ÔþË4,¨*ùäG§dSXÚi,Ê%Ië®Û7msDe(JªbBÍB,¤JeÊdÓQFÖIL°¬³jISj(²Ö¥Îâ$Ì$¢è mT¢1jbU4ÚØ©dÅM·án«âä£I6AkXµ«dU5V÷+ªm¤¬yç^Y¤Ë-,£&ÍXV²¢{FU¢Ô8P뫧ѥTÄ+v³TÁuËI¡*ÜMqM!*×÷·mO¦n"i.S]þ~. >±ëª@6XTy¼ÞV}vj-¬ÄgJâ×rI¦ìÔÅ%a6ïMÑn#fÈ~¿gOÃîZ f+媻8aÛÆÐo#.4D#ðá(#Àª#.¬&(IH¢i¥¦Z#ÒÛ7Íhô{1OL#Â#))7
G{c#N@tÌL!(#)P*aÛ.:Î °Ò×v£Á æ¤&@5ilÔJm%%«Õ§M!Á* +$À±#)ȦTÅLQ-²¶&ÐÂPÚR©3e3,BÛ%T´jMIRm`ÒkJiE d¦¦)F¢KRÍ#e¬VFJ"i#.²ÌJX*+eÊTPe)3fM³jÆ*Ðd*jSVmK2jY Ý%\¤P)IHTêS!#)QÞCK P¡¡`§#)dB I:6CþI_TÄ7§ª[oOga½ñ~#.qr¨7]tøØÀ#.õ3Òmbßk6mQÌ:"y;:wpIJ½áìg#){b¤¢?ǻ۷nGç'¿±T=ÿ§ùGmÑH¨=Û¨h% Å~Ï/³kÃg^8bÀ͵ËîÈÏUЩ¿n@î!ÖY!¾Û4õ6ëíýÕuÎ4Hѱ/1P%¡¥FÄÒÎaÃlÁ;ÀÂ5Àï*¦ª{@<[ÀÜ6!õ Fðf`ë-lpF¨à½0OÕU43èG@õ(¸Ü2¯.¥ðxü±.3Aû¾AïMÖ êÄ©(wvÚ+y4Ô¾t~#.ÝSÖ#Èòé§ÞSÃ#.äê0z¤¥{]kÈ@!b{NÑ6r6ßÕQ»Ü]ÃbòÄûªt4I}XpV_|!F{8÷Îkò@°I¦ø,Bv²Ã96>ÓéÒfwõCÀ£I2ÞGËc#.²¨áFEÙ"o´Ý8\¶Þé±8ºBK#)è2@<!~¥ñ{½[{vÒà"¢¤]À#)#.Qjm¶ù*§oUß<¬C@1Ô f¡À³31Æwæô25×)xóÍ ¾ËÊÛ¿À÷¡¡ei9ÙéØ èâR8%¦ÔÍw|ÇN}üJ¿9Qñ?.»¯ïff`;kÉ6<ÍËyþ+ÔR}ç¯=íØôý0äbQYÑÛ¢!µ.Ô dßÄþÀqä¼3ÄQ´Eøú×µíØ×¥²^NÝÑ\µ×¥úvåËÚCßØ&CeT`11²m$,2%PÉ&(èØ`ÇˬAZ"(«Fã#.ÊÑ#.Hä#[éª#Zp`á$f¯Z@ÍT6&iV*ÍN¹#.Gm5¸«E°M n#/?;Âq$Õ#/p1qÍaàÒ·ë 2Á® ÇKðíMlØ'¥ý>°³=°:õª#.ÆJ-p*U0ÍʱäqÁ#)3o#._ÛqdÖ=&#ØLÉGsï·!¨9$ØFë2;ÔÆÍËl=ÖÒìLijM]qâ_#)ýÞKC)Â>ÖÕûüåÀd{ñnx$và,3A®X°)kóúaéê£b9hýù§Qo}àïzî߯qWÞ5¬W|ßQÇóu¼]0иXZê8«¬]#/#.kßಶð·eÎ}Í
·ßÙkÓpÚ°B8míSæç2"pê»óV#¿ò*LïÚì PáhjCi½ýh0|¤Úÿ=1êX!*IT«MÎþÙ8¦~/zź©#Q© Ð!2Xèªbmt&U¨8°`ÆYG¡àf X¦Þª**ÐÌ"^Ó0án.gCÝÏO|êá.GV,¡T2`ÂÈäW:áG@Qudiº¼âóâ.ZѦë9+*ZÓ«zÃE,7DÕ+^u\)¥hv©¹G¡ª:ÊSKCR,¼ju#.N;¾öíõ&}HÑ,,°¬deØpLª:o,ÕRÔÉYÄTÅ^Õéüýû ù§éÚ©4¢=óÇÌÆÉàLìÒÄ©4=°i*&qëEuɳTf(D÷g¼ì6ðÓR=ùj,a§a;è«:MGJ$gÃ/MEÙw^«×TÝDtríÉèeʾì1î0¶Úgüñ¶Fìí]*wpÍèﯲ¦¯SÂzq©<fR`ÓqI<_yË:N2µfê¼ÂÇç@å±àuÁHØÒD)×C/rí4?.ÌHØÀñLì®`/ÍãÎäÎbÎ|xð½Aîí¡8½àKÜu=ûK"ã#'oQ£!¤ZJ¦¯9YyÞ÷uǺÓÓÝ]ÈÀÃ9¢®³
R#.æLÊÖh)½Ìu/«¾3ƯeÍáÙp¨*Èn¾6²"aª/ÂíÕçiÜEÇ®]®L¦étÔn¹iy®õ¼ÝÍ#.#)ävÄJ7*R(ÓLí!îZ4ìaÑGßóM$ͧÑyhû@ô¯#×ÇÑáZÃsÚüS×ÑßZù òß+Ñ
\zó×¹ìÁÜÚç@'Ò` fÀAæ[:3}±JxantŪbajbu£ùþïBoõò{á>¼¿¦ZAÝMÑ@òJÖÕÊ£[l`/f²§-N>!1 s/(&s `1Y#)ëØÃÊ)#.(¡Öd²Läq7ðèÎÖâÀIIPKçb!²L8©
¾dI¡ofaÛjʪְÙC`ÂqA+yf(ZiÍãM«01%ÈpßT¡#/©HÑ] Àbi°:ôøN¦Ðú
ë4§Rôî{î#ïü}|NåPd]þï© ¹¾õøÎ}YÜ©q¦¸Àætî;º{½yx^w%éÀÒÓ>Ü8°fè±
ÈæäÈ?ä±,#.1Nó×¥àAÄ{Ó¾*´¡¨ ÚUkXÙ¶XR®.m;®Ú5{æKkßSÄbÄ^K\µ{RÛï ÑA!_Ýþ\Ô#)ÀmDÚâh7Ù0à;äÞ=ÙYûÖo#)ÓãG¨%¡®±l(d5×]"É>ÈhB¢§¯DV¾z×#)#qZ\B,!Òn'©>fÄÁ°üÍÃBdàDC¤ù±° =¬íè´Rm ùÁ¼#)(#/#.yQH°m×Ûv~º¥"ïS2k.G9@ã¨G§qçñÄþ>¬±¤$0À) ©#/H¡#/BÅAÅPëûÞ<í»Lt ¦8FâjÑ.èxaåâü{^$,Ç ã¥C_Ý·iö_Qvá áÌT¡ü$¡æÑ£Xøý½]À÷IÀömú:½7Tô#.Ór]Fè(_/¼é¶Ê¦¢®HaýRg$7W¯6½V}Ç8¢4û¨ Wª#/JU?R<ô÷Þ\ *-H¼¥ÕYsSï¨çcÏjözkÎR?tM_<"V3îM½3bºIIÝ
¤~è¶Íôm
Àðnãa Þë|ÃÐLï»ÞV"6·íæö#´pvv/¨°³J >FÞ©èr·`Tg[1n¬Tk¾î°þ¹iÐAyĬ/`«4;;Ð!µ|¶rfo:ìÃò®À>¯uë%@b#)¤È@ÁÂZR0pG%(R8Ú2#)m!@Pá&BñU[øO¯^!kZÑ®úRÁÀÂrÇxM Ù-cP,#/ÍLj=pu7{1a»ã/Ñx1ݱü§v'Åù¸s|áx=çAðÓÀÎå»Ì{#/<ÙT@¯â<ÆÓ{àÑ#."ª¤ßöÀyó#Að?,ïe¬i²Ò|út`EC2{Û#¤èløÐì9hãèC-c¾µçZüX~<ÇÔåÆÛÑÜ|ö,Ä
éà#/D#)ÍîMòã¤0íÄ#.fI¨`ÜÝúN®W£õ}G×÷ty/^ݱ Pf.¿ZMåÈßs2ÉÊ@2£a->2EM+þc?uÈÓ)dsÊ0æ#.«|¤+ü¦)¬Ö=5íMËNJ¹á!iºB¾ÕDY) n
$èD`ôÐØ¡ÃÍ&:J£´d<õã«yÍܽ¤x¬ÌD2R
¹a`2)M¤4Ò[xÒÎ cM-RÄé4C'©Jr®ª@Ë«tÿmµI pX.CQÈë0e² #:<cy27ÑædU4V,k©½PÖDîQȼ^yÛ=yyà]9ȶ9j#.nîÛsjå2½zïOFOR¸bcJ#.afä3¦pÕ§XhÞDº"íþ}fe820³8¸p¢/2дMqk:7Zé8 8PÒ½°&¼,£ÔTÚR@RAlL»_<âÉ#.½GÀV= æD?xêÄ÷hê¯~ ö'24~
öÑí¢oÏ5JÉ3HS1+E:°TÍf&dÄÇvM_ð¦Á¸×>;gYöG¾=Ü^*ö#)£àsÀ¾°ðëêÁÂÕ@¨/åRúOâ D°ÊGa2ZY65¼Ú¢¼êädÚ+¹[n¥EX1,p!!\WR¢tB@Ò04°zÁ7c£ ªQh¤.ügë
ì`#b¡ùÓnc_fúE³/jz:ê¨bÀåk0 \3¦#.A"ÃRvl9#)
A¶ë ®9éùþ|uZ/:ÒùÏõþ^¹í=Æð?'Cö!¢âbëG«®â®k½äYXª-yBäÃJí!Û"h#{¡5cZÍm¥cbº®À(SûFø8ËHG]¼ÝÑ¢#/I¥6©6Ú#.ÊG,v¢ãP BÖ"¸å,åøÁ©¨[FÓF#m«Hß³A¬H²e!Oð@rÝ`w!Ý-n@;À´è¥C©J×.%Aev4P%s0àþDi Á©vúC\ö8scl:;VLCì_J@â°Ú7w»G¬fÓU=§10æÕ'wGeE°)kù{ºiðÒ}ä I±Gìó'Kj»t1.(:õãAU ¸6õâ{û½í$FìÝ3dÞû;steõÒoLPÄÓÃP°Cà GÑ4ú÷²ÀÅÌ)(ÙZ6Êif5i¥e<ÒZJ² ¢JÓéöútéÎ L·YøNU P`« }?rï>}oWyB7GèGñò°ýçc6âb_mùôaÉÊ¡1¤.¡yÆìýò¯ xÑu¾R¸²k¶úeG¦Õ{ º,TIA:µ¢HxÒÅË¡*Ψ\k´5è#.2<É{> ëø>¯P# ¤ªäðËîïËêéÛÃÚ}_=|û c}ÝÉ¥_oYùôNBÍ9°!&ÌIþß×$ÎFûnuü,ò6än˾»ÀÓê©ÐSù³Ð´Í%"èÆ×H?DI¯ïV£TéfÍ@NÜîkÃETüYШe4a³Ä¡w´{~[pþÍ;¯q»ñÚLi¤#.&ÃaÕ¡àëVhi¹où¢TâªTgeö}Ia±Ä8©¤öi
¾ñòÄt¿-dµëô1Ø3ÚÞYXÚ_+Jz5®ÉݼßgÐ"ØHÂPµ&DÈÚ¥ZZj5£#üäÝ4%EK?DsmMÑQE0$"¦Fh%FQ©¦´Q6+FÚÍhikH¦k-6k[¥JýÏ@éIþ§ð&÷uÚáÍ"Nh<#)ü¥L(E1G%hÉB#/@¤S qÁ¨°$ôõùû8Ø¢Ò¤) ¦$ö!;#/ùâ÷^¿¸ØÆìïÒ¿_^PRñ0,"5ñqÙ>ß¿bàå_NÔÄ<лuõ: ]xüUWËt¯üï#¼!è#)È~PÈçاÖé§úN?%ýàa`ºC$2\ýPìBÚÅ6PïÅ~ ½¡¶;Hiñ{¦Ü/íâYÖv]8í¥P±Üò½QEzutª'
ÉS&® ¾å¹2{çË\&¿¤#.¤
Àñ?1öùÄÒ9ð7}ÀøÂs¸ë<lÌtXm©ÄÀtêxy¬è$æ Q`pBÃAØ#/»v/9,k9ÄB ;é#.ÐÕ?yâÂËjf"´Á¢AÆbPþúÏÍ!pI4Èú<6Ãs&;X>/¹5+«ÉeTé]û8ûb¶O·¡ ñÑú¶£a4÷#¢uÑ,Õ´e$C\[Çñ度I#)£$ßýmxqÏ¿ÒͧØNS×9òk
]:ÿYÉ®Så!¼»^Ìummj)[h"#üÔc!Ï¥÷lq~\÷~[®AÁ((ð¤&bA&IÖ!éQÙdVÛ8*scCAÐßþ:àiw9xa¬Ñ£B,PQJíTlòZ¶2&*e4MîÇo,fRQuâyäÑ(S[íi ]=Òd0PÒ,iì01CL#f÷pa¾ñ-îß5u#)%ÛjÄ`ÆÔâOåµBçAÃü»· Z$4C¨E¾`Ñ$ãÈrù<:IúWJ&ðèqÂ5ns#zjƬàJ)ÊeËGC[läÚXͲN AIHÍ4fCÀ°ÒEìÓµæ%åÖz»ÑÂÌBòÓ&:M,²À¡byÜ#.;#)ãë<{ôz=å Õ··@jëõúN#/BSØ{föÙQKG´,Gs2C;ïn'~úqöqóv#)aÌä2îiô à¯,<ûzqâºÐY å#)÷Hi¦lôÒ¢Ò"Zõ*]×&j£z\8g4O=Ûɵöp|v¨CÕÝ PU,BÁÞàQKHùôÎЦÄe*ÝÐ!ÞÐ8¥¬±ôD
z|/·DÅs[høf#/-vÊ#/i°évÉc9Ҧâqs8[)% ºaú4´ÍëóØ÷£]nKîïñ¼ð÷+T#gÖ&t Ä5ÍÇT5¡ÇvC@ÄÒÍ1@pLÆØ®®\§îfqô«ûiº¯ÙÀâ¨q&÷O{Xߧ²¦½.è°óÀq$#ýÀá¶H¶ëÈlÑÔ:ÁѵÉUS#/nªPß{϶}¦ñ#6ßÝBCe<¶¯À1È&æDØV"ëV#.ðÑBxk£EãIà3OG,¨9àè$A½%,J~üéÕF$il]×Y-%eêq|7½Û%BÓ£T=)NnÛ]6>^äæïÅ\°ç®UÉT³¦c¹çü_Á>Æß_¯@È£]HP,.;#.d\ÌE#/Ù0"U¥JÑàQQ!=Mh#/cðò0=ñ§èHèy=]¿]èüà"ø½}cîÍÐÉFÞF0?¶]~;·SÁÃÅMÎÏ7ÅØ?É($@)ÔwG¢G³ºÎîí¢pQÐì²ÝæI¨ n~,ûõoC©Î$á>wpö*üOGVhÔBV1T@Ð8X#/#.Ejm¸mØb!ª"ã#%\#@**¢eQ3ñýÞóÞytmîú&/½¡i *!vGx¬$rHÐ fÙ>zX4BL.ùp<æjra-)©Q»óáÂk9\uwXKnà 1¶¤i3/2ÞK%«ËÊM|G¥W§¦Ý¬2M°$:ãQMcÐR&7¢mpbGdÆR³ç$¦äUeàjèÎ4òîêôòÈmÖÕ"j4ÛfN,Ä/íÚ´ÐØa¤ãs`Ö¤UD,³Í1wèÈptê¤q]j{àOñ÷ é:ÓÜXâv¯//¹~/Ú÷þXi/òþm¯å>wm?µðªNf]8f£ü\ÇS¢t>Aê#)/×ê" 6¢%}¡D¬È@PLÈ¥#)A(zàöÂ>¤Jë§v®X×5ºh×+Eú¼!ééý<ÈÏÈNöYéä£.ùn¦qòw³0ÈæßÏÕ`²Ögß'¨ÃãÏìåËÁUàb{äÎ7L=ýA1÷#)Hl$,L8oN >|Ïè°ërÛmdâ¤Hº¹{'ÕUm¿Mm¾QIÉÈÎ8Hù©A(0çQmZIáÐ8Y6ë M6Èm¤.z #)D,¡óTÝ!ÂyÃÎÓOI͸BôçûÀåË1BÇEFúáÇKS$è¼L~iÖrßÌÑLïÎø<éÚ)S5ô'Õ4£é³ûÒüqMÛñ#gz8ðCO½Ødòaä|40;ýù×£ì"'¡|½øY£C%[¾¸àWKç#)Õ0rÄHg-nÍ°ù¤ÄßÍ6`yzPßE9¡¢Óp£ÚDcÝ6BdQê7 l´æÍ#&\Î_{áJÌjjíÇgÔgÔ1ëCI,",ªÕ¬è·4#.+Fä?*sNødÕ¢=éO&+#)É>I!{P[_óóïÃ>#/èm¤3Ù¾N2²Xèñ,Ô{-hÄñZ:ó)JZSh*:áuvD84'w7÷#cñûºg¯D#)È
~K°ºN¾ßÐT·÷¾Ô7ée¡uRÄH*rµ
Ô$@qÛ#/$#.ºÛ 'br`*ý¬!$Äa¼Yäé$q6OÒÆCP¤ÔÕ¶33£ôI¤·¤ãwyÇdIñfh&nÁß0<s»=]Ò`¬iIIµß&=åFäây§xR¤,YD #ÆRk9é]"sÉ3B0ñ(ÓcËU±ëóhõè¾°JÇ>'¾øw½çËñu¥*6ßVò!ØýgvQb(XYlJTÑVúÒjCs0ÈfT#)"¶e[u[7JRñrMuXâ@LÒªBÐ#/;nöc¶óÐ;àxýsCOÑ©Raæb6#ÀHçãQ@¶MR@Ϧ¹¡&¢FHºýöæ¤ölÏÀc4Ì$¯g¹ËâlM¢3<Û£R5u%"0I2Ëþ¾JÑ'r¬ÌG}ðMÍdÚSjÃAæp}V¬èbE,
Æ4ù#.Uq0Ùó`ÝLpC4îl³E}ã/ÞÓÓ¹öb8+ö§î>»·ç¯§µdî:JìI¥ÓØÍsgKvZÆ|ïåòAi&`.fa³~}¥v¾ç]?x¿}%sI m
wS"ÆÙYü×®ÒàÑÞN&Ãcø&,±$õ±ÇÊ[.ÖFD½ñwýî~ïðþü-kè±Z&|â5¢QdtÉ@ÓC$µýâäb1#.D2V¶Ää?i«{@#.YûöÜ5¬¥+Éo`PgSPÕÎ5ÐSçV¼J¢;©*®ç"`®úZOÑÙÝÜqAú,|i¦9:Ñ3D¦5õV@îæOâÔ÷µi& `+záÒý§HÝÈy3d(ghc"Ó5ÂQýawLÐÍî¶ùT©æðÈÀ:ìCB«BÃFØ*Ì,°óûÏî5Ë29#!Ì6#<Âðî¢
ª¡*ê[}½æqX_vG\°oïÎu¥YÒ·ÏVÂjf zË%È2gªÑÿraB;)ç=¾YËbB\Z{óÛ0t/ÊHÍnIèPÑÇð}Ía äÓæ³ÉÄNþ,m1ð #/Hi#.neÐ]=ômü-|i÷q=\ge
ou·I¡¤jì}ÓÄ÷Ò[Iæ÷õ?]ÞåÞ_ÈAæ*"7}/Ø; üÜöu; bwCJ©UJVZP#/DE5d©¢s.Ïn<Mx¯×Û]#/¡dÛùd
TIÌã0 nkÇ^ÆYZ!D6Ó¬q #.÷#)5#/$y¾Ð#.ó»õñüøuuÞ
k¯ã,º):ñ#)ÊÆ'@TjºeHVÖ9©ãyào#.ÐíÒOépwOÓÌ>¡ã kt<§ù¿ø_Y9#)aQñWëíæèsÅ%t#/apPxYæQ7¼Cø ³#/ý
Õ#.¿Õ6Ì.3îw?ÑÇÃl9è#TWÁßë#)Ìó1UHLÈc~%"üSWmÚQoÛµí¶% ¿ê4Úd>'_¬#)³ÐÞk{JÐÃ>l´
¡É#/±IèܸPú7ôùÛõSÏÃ"¤4å:È<Ò¿
©5ÒÃ9hÄIÇüFÈb APIÔé8Ö?wê°&Ä6Dº±Dï²ÈvT,©kÅ>Þß^höíËÒWyIþfWÀÌÅÃ(Ìc*"båÒß]îÓ£5!²µ^üÍy<M¶Þ¢Ï´ã<³ øùðÓ§ig1?oó»t49UG«q#.=C<çôúµö÷⤠û>êì.¡$Hôi0v)¶÷#/Ïìçöw³7³È¥iáeûØ¢|6:ö9uųÙÙ±¬©»kiûD8Îbi6VÐam4Û¬»#/Dµ`SZ4ö¶¶ÞÙ âm#.bqYjuè66hY
3«¶HÛL´ReE×uÈöbX
£)cíÄÖ¨¨j&(fÚÃgrÇM.o¾ÙÈ Â ÓlAêÚ7&¡D2hª]âf89¾ }0ÉHFÅl^Û°K'Â[#)/÷ù#/h&)@£a/QAt껬´ÊY4·weдKÊÛia&«VGV8@h3$ªÞTpm:ÅÐ#.MÊbÔÓKe$£&6¨Pf[6)j10ÕJZ^ÝÇ©ÚÊu$F4CP1±F#.P¬-FÒd+S·ÁÞß|`©¡ êt·%0dc9ÅÌ´Öå00tm§G³W#.!4 A ²*RáeN@25º©8áSoCL»P£#.4lQÖ-Æ+¶13q]f 4ðg¾}´PÜSpZkC&æ,¥TDQí*T(³S$ÁrÀ1jKÛk
#.½wJÍ<ÎêmÃDÁè0Ó¬"¢¤Û²ë,n<¦0·4HîAFøî1B¶¸hÈ M#.#/ÐÂTBÑjX²)X1.ª
F)¥cÚ]X4Ñi-+HIÔæ61¡X¢Lhc3µ3~̹²¡
Á1/ J%´]PÈ
áÈÆÕªM*Ð1»g²î{°Ó?C¯ovMU
=Dx#.7&ÈxE×ðdÍl%iáDÉìÅc1>3gA[ÊáUa#.CÃbðe¦¼sÏ#/½&4e¬DkCÒ7á¤bÐh´ø1ب޼Þä44ZJßÝpŲL'£$â~î¡ÌмoZàÉ!ÄÜNÍÁAÕJð¨K|jB]Ä0 -1)ÂJd¡!£#/V%ËhO0EFX¶AªÄ¨ÆVo¦Þ·¢HÉîBtÎQY¸ÒÌV!c
gè(ëL.سZo/GÊ$¿|XAb R_Ù0@»C]H mkPN£n ËQiHÆǯ }°¶á.Â<L4&V Ö@é 0eÑ#.d"bQ62&D"¢ J¸FH«t6¾ê§«·»Ïñ=ÔtE3å~Úb®ñ~XCçúæëë`=îk«÷°òj{¤6~24p#Lx}¿rÞôk9ÏëõnzÒ7!Ê?dLJï=ZBÈw~Ý»JªhJ
éAjÉ¥z¸wv\EJqB§«0ÉG~Ð×|&ß5¨¤¢pxx KSMTðdNf½{ì|ïFÕl×ò.Ì%Ä(#q$Oqyèá½(®ùéÁ¾XMÑÈD:,Dn1#¥¬òÙ¥DÒÂ1ÁÂCrÀãMFÉæzeÊå«ô¶úñð:lç°º(êQG|Ü"¾êÜ6¸Á¿ârÊ~ÜùÃ[ÃÌò*¹=QÍ};3¯dtç!^\08|1Ľ}ªìd÷¢3âÍÇ6ÛA"Âu)¾åU|d6BFbÒ{a¬}3ckܶ@âFùnúÕöÓV=/p;ÆN\Ô~?¦qø¶ãëS¼~ tÚRR¨!è?5#/Dvc и)¤Òá7J}Ø#/º@í<º³h2·#.6TM$:´¬$YuP&M®ñɱj6òêK]µNÜÍÝxª+«Yf¤Y±³f¹Â§uZ+ITªËr\%B#DaÒä¥"º,0]PybjdûøCMÑéCÌvªS\Ï~ë9ÉÊ£Ê@ F7U#*HÀÖ/BÄlÁ@téc¡g¤-6&êh(
M`!ñ»9ìq-I`!8ðB¡ÀZ/õ»T2lVLÊl[Ñ´ÍFÑQTVÙ#.Ffd¬ÊI0¶hѵ&¡FÛM@£ÀÊ0:ß··BâàpOGÜÒ5ÐM$R¢jt¿CÍt½kÛÆ[ùSG;ÉβM#øî¼U]³¥fÓJ@¶ID&¯M Jö@úeW(MJaß9yuÒj»©6»v¦0¢Ä@æ8$ªÁ´ª) RKÿ®ñî:÷Ñø7 ó*÷&*âÃÂ#)u$Õ!Êk׺)¥b°kDë.ýiÌòïë/* ì3×¾<ÐÈX8>Ócm*pÑ鼧êMg³¥{wö`£òá¦_-#¢z üqEÞ}ö¥ÑÄϬ'-c³1bÆ1ÈVðûìªH61ªðcÙ PBÕ«2¨=²º$ad§ò²õâ¹:[¸|¦¬ öç.0XfKyÇ/SÛë1°èrJÑÐ2]Ønú[¦~·å¥±Õ(àctwaI¼e0C±v9³W\(6á¦ó~«ë²,NDjº§;Îf²u,¢/jh%¢°Ã!V=º/6PdK%´ËºÙâðMþpí¸¨*Ä~DéèàÝ£ÇòØóLë"wÊnÉɼôç¼Æáþ±ãCúV?@äÄñì#.&#/y2o:;66¸÷%è¨þKìAù$¾ðäÚÂE~1#/(P#.D/ 4¨| 4AACýÄ%ã#)abwÌ#/#/#)É4_Æ'¨a§_áÏ_LÑ(}^:ÜÀ}#)ù'
}3ÎL\ûHb2KIÚhÉ8J#.#)@;A¢6ÁihѨqÌì¥ Ó¾æÛûCn*Hl¨æÈ#ÛU 0DÑM*®¡ç³@Ìr(å*Ø¢UÙT½ÆîBaÛP¯vV=è¥cUá@b#.36ËFjÒQ² ¸«XDÌ1È@aERDEj`¸O"¤$5ôâQ`#)&pQ 91xÒ>¡ûï·1 I4ÄÉí£~»öðòq%'®f÷D?yÛ#{ §FMBRs¬íbø,IÇçÞr#. £ÅãÇøyz;ÀÁ#)C¸T}F¢bd#.m&bi á¸ÕO¹¸ÝÇvM3âÙò´þ¨Ü×¥§É¡¬@¿s30wMv#.?rÄÈÊtü]ÈsÞ;àÇ$ù§lýr4¢gÕüüÒÕ<ÕkðFïFúgj\cüÜs[,¬é$ES6s|¨\{7äÙ4¾M¿4ÄÍJWD,¢yS? õHäZê½Õ#¾>Ú}04~ ðX#TRÔÒ[r@ ¶Èaä`±PhfBzµ¥]G·ñ*&óú±31ST?=ºVõÁ{6Dy¢±Ðïð·\<?¾æmÙ£#))%j_ö?hÇPó&óRfXádHà!ý»¼W$ÈOYä@WñrnÃhÕ&eTÊXÂ8Dß Ó¥Ó÷¾¾Æ©¥£f³ZÒM¹RFç̨SºójeF%±ÕxÖëKX´PIcFÁotÖhîuy¼£¥Ö×6îíÒ+¥âÞ6¯r$Ñ£Ë!fJ+@¸(j×5;¨Ö¢³MN×T*Ƨw.»Ý×[¢ÊÎë¶æ®¡ÎÍÄVB.$¡Ý vºAMWëO®`ê7N aôõý]¢üt ù½A@èãó{ð(zÔÿ7#.J1æÙ#/·Ø¡ëe#.Üñüê>ñ<SÓÄGßû'<iAÐÕ;"Ø)Xïôù¾¼õf9î³äkê߬ëïL±ï[7½ê«/cÉá&öÖg¤=β%Rª¥@¢ #©#.õB&\k5ÇüfÆü²Lr¼ÿo~DܨbQ¨§ð$úÏdm/Ôª<r1¥ªX2À0Ì $(YÌ%pôiÑQQ¾7×fcÄã´¿"@N1ÂP-bUH&1-ªù½ûuËW[A¾àíP14ÀRj6Ð`E*!!#.Îtä@²R4 ØÈ'÷óç#.B}VÔ/ùü__¸¨AÂC9(ZPÉ(,Éõ?Ùþ×såYP#.»ÃpȶhTaTdZU a#´#.¥xtüfÉ£ÎBÄâÈVV d#/X Z@BT#/|}ǵø 'hJ¡Ù~ͽ墤ÒU0SJe*ÙH
P @FÆÁÚ*/úaßÕ¬o¿<.ptCë,xÀ N"Ô?o3#vèH(XPíðÙ+X.m¦û:|rÃàÜñ¯ê)à¿#m¶û¯Ý¯#.¡Hä¼Pz°ã?;0pl&`_×ͺò#)+!!ÝU(k|ÖÀÚD4¶kLá´38ù£^-dGø!a¸0¬
qk0TLF P$P¹6àë4á"ÓF*§RÁu¤J7Ac³$*FÔ±âÄX@i lv¸#.#/¶Ä6ÔxªåÅ\£oVjÕÊÚ#/ÆÑ¥ö¥clÎs'G>OmÍ]]«ÜªÁÀ8QlÂM Ì60[K¯#ÞI#.kùEQ£MQhÓÕL ¬µ4d¤`ähÛc&EZ@"iPÀà &$Ð ¦-06ì«°c°Àhv9ç. 3cäòfÖ°ûðo(¡ä:TA'"Zh FHÄvtÈm££Ëu¢õÌT(vtHlG BI ¢¿ëTüA!Æ>øPÈZH9 ÂZ21I9²P+\ÑÌ÷Å'8¼ñwÜ#)SâSþ^<öUîìX£i$-|`´T¡ÇÊSèÍ&Ë\Häc#@66Ùd²a ¢úU1ÁFL¸W¬alÿ«
ÓifU§Éý'gýR|m¡óÈ:%Z-äÞ¶ç#®Ç·ü÷0Ú²Rõ6¡µ6n×RÉm#/Æ9Øa1éÇ#¦Ðõ)HÕ¼& ä¤wôªPØxv" Ø©H¿²A#)£#)û0pQ%¯#)õ4öM¥õ æhüâ¡î_ÎÄ-_ï¯kHc·¼8×/8öÿ§éoCâ"JHJ¤ /¹BÄB]ùíBÀò°ÌÌXóNh\=Û¸^ÞÒmOÜdÍ樮~%44j@¹Cìû¦Æ¯¿7àÁÑÓ (z^5!]IéTÝd©W¾ 1ÒA©>©ÌfüùÕrøÖ¬Í8Ì~©qn»5ÈO¢5²-aFyÐ+#ID ¥ ã
2¦ÌÍyÜM£ÏàÃDZò$ÄH1&áÐf¥é0pÃySèB¹Û¿)0îêù[ïKbücÚùÍÁ¹Êª£ÚG9Ë(¸Å^ȨwråÔ1üöDÄ#.øÖ{VÊgBÝY<o,?¿Éa#)ÐØ0 ,#.iÔYfÒ4ÔÊ-¸áØQMëhÆÒÓi5}RI"'c¾l¶d~ÌÃ:4NxÜ8ÖçØÄ«NYÍt¸õ#/l½`±qRºNåûCÏG=¾nÌZd}hÛvGA÷8×)k{#/r§©«Ó¯1åÜï)ß²Ú¤LýÓ*öö!þ¾s§°±, °véÆÌILÌ]Ú$p@e!#/,!ȯ²¹ñæ<=½Ïd.ý5ëÐåÛ©E:o2a8!èTûU ÀV¦#@TSÄ#.ü}¶iÐpLqÝøÂNä>Å^ËeíSàx¥ô7ºõßKÀN¼m(LÍ¥D4ÄbÝ.q¨Q¼ÒÜåÌla«@l±ÙÃÛ$gh¥3\_3=©i=IÔÿwED¦$ÃP B+>éÓ[5ãcñæ1ÓÜ`±FØGii¬C`¢a$ #.±Ôõ÷ìMÚlA@Ú2=qY;Wkôkáéôg¨MQQÃCZO]iUVQUF-¯_·:Àf©¨³êÛùjÜUä©6°îÕÈFÖã¹#.RÚÎÊa¸ðs%~#.{ðÖÌÝvï¥9:,²HÿZ<æÚRǯNþ-IÏv.Èá}$íU-ñ8Ôáιi±p¡A-¥ÐÞ)¸ºÛÈ ×=M°Â"#)a #/E'x\¢3°èaÃN.§|Fëwëfõ]¤2Ç)¶]ÓnIñËÒ0î´ ë£ímúeÕ°'VIß~:÷%TIÄç8Ä0<Ã]K`~ælÇ&̵92¬PCOF`ø7ã!Uw=]+`N^¾8gN^&.KÇé;ªª©,ºØ`>ûC+ñ×Í÷øü+ÖëÔ^¡vaJÔ#T¨{Ôþd¼ÛöÇäÿ;>D¨)@ÐÄ)D_Y¬ÙPêú{¶;i¿9úñø?Ühë`Åh½ñdÉaÜzçê;Ú¤ÀÍüÏ6£´Aʦ#)ÌTwô·+¶½Ç´¬#o%òøq;ýáÊEwÒ:>ÙéÉW4Ûëa¬Î9lyxM?m¹%]XÑOzuÄÉO#)Or)#.6þêûtJëË_CïI#.w9Óq3#.ÐÍoeoÝpðoÞnZMzý<»r4+ mz°}z2þÕ¯fàE#)"$õÈ°##Ð< ükÁÛé6;Xá^3qô;ÒЬLzyPäÈAôDaV_Ñþ?±NøwTý0
Ô#)¦Ù#/JvpCº^]^ààe#)rUЧ4î¶$@ÉÍnhúÌ Ú]
ÞI'÷t¾éøAAS3÷¨&xÌV|tÿKHÈå©èT¨àÇBº;!X
zÒ4_G©N±ì@/#)
K¢Ô#ÜWÎ#.ÿ¢*Ô¯aU(41üÍ4L6múÜ#)Ökí[æ¹5íKºIZLõ©ù}>#.@5Öí$Ýók;úßág#/RCºeùʤU¦Ün(ùBG2#髬Jc¹>Ïæ8ö¡Êj:q0O<ïC4TV·¨»¥ È¿#.
Låøô4x$;9-öP?YËQ´û7Ëã4½:%)E,ÔÙÜQ®¿V bÃP9µbÁO}BF1~Yts.|íaHÈ":qôùpâÂÔRUHÒú@)h BZPPÈ÷rápå:T u4>zý}ÁOâ¨!=ü>ܦøz-UT³3#-$iIB)°ÃRJ4ÉÙfÌbY¢PJe@IUTä^ÞP~)*@iHH;u&c±6zêë¤öDÔµÍ\»qµµ8msÄQLà#.s®Z#)cM{AÎ ;ú,¿¸¾ÄÃ+*qÝíc³ß©Yyx²ñZ ¸c3å9ÌöNÚMâìM#/ébÕeÐТibêCØ0Ã9ÀPö#.E#.¤g(¢F$f£òæ|É!Üwõ!®¬8IbpáEkñ"Ëéդ¼÷$³¯uüÓáMP¥Ú<úþa×ðcÞ¢Ö pY¼"çÆÑ"|ÿv×JÉÖ¡²ØÒH)?z«mdÜ` àF``+b¨he&q }vÊçQjå¥uÛdÙÉsp`Äf梡H,MÍHA0YjD¦#)´ÂF;á@ð¡uDB;JDÜ~LÚ*
&CRäDàãCPbäqLÒ&üŦ=#á0F´0AX¹iA=ÕÃ4ÇèUkrBÕáTÈ3VSüÒ¿o¸ÖHGÝÖ±ó0ê®Ñ£¸x1øD¯Ü^#/ß,6ŨÛE(òPNÈ,2XBµMM#.&´g@ÉJ#.H0\ÝÅÙ)ºé×OVÝË(íÕxÀxÙ9£¢SFiIh!ÜHVè&6°pP#,E$Pæ(°ÌÕ±±&áci,¤sYTL®V,ydÊ̼$Fn",TÊ4ä@©ÖÕGJ±²më-(ÍHc£qD Ñ)4(AÕ_#.½<æÖÓ¹³oZÖo(jï7»Iî4Bbml¦ðS.]-OÀåM¦ÈbRdum[ºQ6G>rVðh+U8æXd#Ã!KÀF@zÄo mMaÉjNí¾ZTÔÉ{$z!°\
Fmd¬éT 5uCtµ22WF4<Ý¡£TÆ6uÖ¬ÄÜQÒE½³DÀ`27nÑ¢ç.r0$yªJ±¤¸l#/F³pa£IƨÀÐÈEè\Ò[3Db¸àp0Þ¶8\m àÑÆA_/Î@'¤×BõÄKHoRÚ,ʤ»²#.T,`eÕË][¤ðßxD¥Ú@È2Y2LLÂÊcH`ìmV$û¶&B}ÅÄ#ot7ÀÇFµg #nVÆÉFÖ:^²12"D¥
+ÕWãÛJH}Xk#.#ÃXÓLÛé¼#/ªl¯JcÍLX0Ü#`¸g Y0©4Å2Æã[¤è`FfÙçcÓÏ0æÓEÎQÖýód«ÀÇ|@,=¹3#.J4²lFâ*¤'¢<ðá×^àè5FiEµmºXâ8×¹¶&0<]4n#-8ÅRE!gRf%£ß¤#)Ò\@cB(m*!¨ EK#)Ãj´»@Ðw9'çìí,©þÉ4þ¼È2mfB¡ ÛLòºH¨B0Æ«59Ìèa( ªT(¤¨BZ!&Z&!N?ÈTè©P?Z|ý`(yy/àôOwîí×ZTz ßùaÃ@è7öo3F »É»;].Zz!Ç* `2fX
¡RaL9§i+± ê9hùD?3ÁÂ{øì.<>:k*h#bS¿f?yÜ·"sQ.!Ê Bí*,7NÙζ ÕÕÒÚÞÛ¥Yè!B<\ÃPy3°`ï§u´'®¼òQ±l_#.ñPrçk9Xþ
,òÎÊ$¸njnËpÈÁlÞtB8ÚS®aÇþU ·KßÖé`oy¶5Â\æõÃ]O¥Ä´¿CéåcØA#.%¹³#KõòZýÍuhÃL_BÐ(ç=±&EüBúvÌø ñ:2¯ìþÔïÜF ¬Öu/0èaÁx(a£ïf#.&¢Ö¿SÈ$çxeô¯SÏ`=WiõÀ3ZÃ}ßSî=#ü}¾îÌKñüRÄVìø0Þ¶Õ=ì0ÙcÐÕ²aeÄæ³#/¦ÀõkåeÅEà1'=£Ád× d!±(ÌÅT4mkí=31#Ï|ö»~"THXWê#.] ʨò+¦z&ªÿݹ²?ËÌôÙË«lO#)ÉW±päpùjêT >¯ê çVëÏ)o³ÄíccT8î²TJUÒSÝ°p5TG>é7Kï?GWY«rÒª"pbÃM°A@-#/a³ )¥Q6xáØoÎ' &¸Îþí)RRBsl#/©ýUAª¥íÀÞM4k2W³SX`ãT¤[FAK½:ÜwvhkQIµxæ®Z,oʼmX£¢V@á#/jRê@) #.k!ÿa(ºF- ´¨3cDa#.Àèz4TÂØvÕ"}#)ÙsñN_X{ζÃ#)i¤ #)xÈ)í%u#)âªæØÖÿFãÀ6q&NÏ.¿NgíâÓ=Ïm¿CÅç)ÜÇö(Î@éÌ¥þILÑøó¢§d´F7W¯4õdB8É BáS
åë6ö`.!Ø ¤8Ñ7%(¥U#/q¢T2!ªBDè&©4éXPÄÜ'y¼=rB¢xQ"ãìÈ®BÌ<ØöÕCàªÂOò?nÆÀûÈMÅ>Aî3᣹Í#.Þ#/ <' |¸·x¢^£©3oJ¯àx7ÞaBxâø_PqeõÈû¤DááP<ë5Rf¥Ee6R"4¨ÄM#.´Ë)Ðʵm¡ IZV@ hX6#ÜMRØ ³ VÈLb#er$ØO³#/\ÔPËâ(Óc¤d
a¬#¸!+DI1ct!E#)l5Dbjij#/34ÚEÆ0m;Æ죥d*@¸B̨0Ð#.¸#/©`ãþ:ÙwòÀt #Èc0"îa# PÌ5g¨íCW\*>\±¾|>Ó»Ìëðõùg#.>QUwõ³Jíû²e\ëi¾äá,(¬¸µfc>ÇG(ÛB`3eMeK(Aí:Hëuí1ÆC5xRbHÈ-Ì*1¡²ÕµÇÁ:Ì¡S¼)ÒEff¢§BF@LÌíÙÜG$iA¡i\p3cBSº;·cÿÍ 3ý)éOý¹ÁcZ×jkÚÅ (ÌÕ"»÷kffå#)m#.Ú#.@çÑ}`w>Îç]ÜÊ?§/(í¬Á$;7H3ã¾®'q#²Öúÿ/¾¹¼Õ6öé=tDÏ^qPzig;A¾gDívH#ìîo§[£ó°t §\èJâ-¥A#.4.Äë|o²<\!5×9§ìv¢½
4òEïÁö&Ç#/ÈÁÞ'Ì|Y§*;¡ÐC'R¢iÖ a)(ýaã_Gh˹Úä;¢uÝ3áøD bà®ß±ì¦#/°ÏôÁ;8Æ-58ýß;®FfnÓ%OÛTdÍ{xȩ̀å?
às®Z¶4@âqº;hDôdS Mµ®ü^æ滸8¼Os£Jt7;Q k©Ò¼ß#.v±1ðZ-ÎoáªóåîKèܵôô¹:§ç!/Ëwß¹÷ï ý1)ÖaÙóç×^ RçåórÀhG ÃËÃó·ÂÑFDÁ$}$R@;Úf#)ÚfKtìÁÕ03ÊÏ['×ÛüuíLR=Ò·äz»m 'óÙ:X£È]f#/rA(éP±þ.áãÐ îþ2kn><
;¶0øôbJv&»|ïÐ
aY~¥Î\[2ÎJ_~níeSLðÇr1=ÕIÝÇX¸(wö£¾Tu1𢢲TÞæç^Ý&ºwMôHFQdÂéTOX;wÕ°vÖñ`äØÉÆjn<]õ/qÎs%Áë¸}ý0êéÁ0cFÐßIÕI²´¬¿Øª×BM`¯ÀN<¢NtNÎfp¶9òæHunÎJCÂ%¼¤êjí¶pÃwéÇl)8g4a´cEar#¥CIº/4ê¶À×Gtøhµ§7k8'Z3-tyM#.«wÂpw#G/®Úö {I¾¼¥<ó$¬Wrõ½©ÑóÄÍyq«ëÓñhuz&
sg>ÞbÁPã@×Ü[[üÏ'Y"äø÷ßuFÇ<Aàäç©7ë}ðiï´»w<½¶sEwv÷¦é»ýW¾$Á'~îHñðíD{*¼v]þ(wî±è3tOß#*NÇì¹ØTç#ú68mL£<£ÉäZMâJÔõ¥Wb-÷Ã)3Èt:ôm_-¸ºLxMu¯ö¸V$AífºOëyHñ~Gz#/Ý3éõªe,¶å³\ï°7Û#.¤C q¢#.&/"¢+]$r(FÝr1¹jÖÚho#£2)ï:f°üÏ#©Éq¾£·°³Ð äFªc"hjXÁåGbDqS"Jç}1`Ç£¹C¥×ÌìÝîCÚÎAMDQ ÛEÙ#/
òI@äöë4ÑØá%Ï;:1!û/88Ï,Aé¼³TO_:0K#.äøà)~±)°â%nÊàp¸¢]ÈæWFÿC4¨Uâ.MÀÂÀ¾ÝC»@:ÁD&>>BÖ$ì]£CDB¡Û°ÑßÂtrÅÀ{rÛXÕºòaK. £79z²ûk»ÜJ[i å¶É°v²X^Mb¸x¹lhD D"#)¡#)g¡cªhÑíûiUsfvÍaa´p#/,ØMûëÑïñ7#)Ð8pGd@0{lÛ{éËYZ$¾;>¨3C ç6Né¸Ó³èGòCÖoI'|Ò\"z§¨AÖ>}Å
ÓÞwìíõ«D´Á©Slä¤JLr\p÷CýävMd¸~¶×8[lÌ<o18¨ò8r]ùÇRy®è»qê(=Áøw1'ïlê%â3®'Éaí8ñÄUè;'ðç?+Â3Þh&*K4L·KóPtcbr<·{s©ÇÓA×£ôÂXætr>ÎUôö¦¥âÉK/cºãæ»Ç<Ú.Ä%t»S\Á;§z;ÆaQ©ó¤û.5o`#/$Ñ#)tïÒRöá!úpSÄæZyvvÛ½Eò5ݬ×IÆæ4èímÞÙ9â2#.F5Þë¡ ÑïOº¼é»Wæ+#4bô
-²K6¶Mºv]VU`¬):l½Ö=iØÃj¥×Lõl;¦Ö¾8[,³DBæ¡°VçÞpê8¿ÝX1³Ê¸£ËOÕPùëÚ%ÝRzÑI« P{¤BÕz³=bÑv¸Î6e*ÞN.Eú¨[cXEfða¶##. ú$_Q[0^çOqyh÷¾ñ^2äjS¯Y4Áý8úi=%¹dZÑV|q?gNB:ZQJ0IIcC8N#/©ÇpÂ@H=W©êi²´V0úívÊÝ{<=zS:d´`VòºR$e¹;1F@¼,'Ê·Å`zxèPIĺÀÁ'¤n¦ Q,¹KÔçÕ|`i9¤õN6C¿smØÃÖçD6Å®MиÂ礸hk²WÛÃCº)*©2Aл¯Äæi#. 0S´_2f Ôw3 ,0JÙ6È7hfÛ#.¥3ÎÎqì%º+åvpÃY0\Ì<g§ÑJo'Dyâ ü0Τå|QJLzyfägjj£¿óèæBqæ1¥ÕOzÂ{4Ã4_;|Ìmû'fi 50qíÖmÄLÐÁQÞ2®®À5^¶-5°¸m[Ç_â>y(a Hd&èDm0Ì3V0x5PÍ#.4Í4¥¢ÒU1iÇN(Èb«oMh Ö5ÍÄæ×6Ç¥Ö 4´Ó@>Éfi©«mé®Æ¨ÚµÕÐ@¨Ei>*¨*ä38Àz3§7WÈÇ:mÌàcgTâÝ2Æí¶_ä×Zbu#/F;eC¨!Ȭ´0¤ª¡ð)(E\d7ÙÌÂÍÏ~úvÊFpÙ÷Ã5öÝcÇ-.RSHaüaØ,ëÇ^TbÁòÜ4Üü2aÃþïå0lálYÑ2|Ъä;æ4T(ÉIc#.pòµÓcP)Kϯ&R#/8H©#"W´Ê FË=Hîx±í¢0 ÂdCÊðÅvè=HWM¤Úlh$0¥j)ÍÄU9¹FÓc±àì&®]uªcWc;ÖúÌζ֣½ÂÅ´HJ8ir(#.ªXb3%¡ÈØef=íêõ¤âdâÆÁ²ÄfÈW+ qåàÞ,æF#x¸ê9|¬Á³Í¡¬Ë82,u®5µ:Ä77»L¶ÔM¢XjBÂYw-Ö®g^¤`*uvf¡tíV'T©aH# Nia#)j#.A¶1´ÅÄ#f¤#.0ÔÒíRFT.¯&¬×®%:5qÀÃ%Ùä!2êT1¶@Óæ+\ФIÛR¶Â7vÕÛmá¦o´¨#.)Á6RCc´È;Ô¬#/ ³#)È´8%S)TTT*ÃÞ Òic#/Æ6óQe¹yâéhá
ÜÆFÆ.JCi50um#.7IÂ-Æ9¦î³ hrÙH4ÃÀv3k?é90²üÃq9:aض@êf,yµ#.ªQ¬Ë«p&3ÜA.âà7f]é©H®qt2ÐJQK#F6È5aB
hmFLD#.¡$Íä墩©¹ç ÖnÜÒ«Ë28t"'®G¥×F[ Õ¥iGʪ©µ@¡Ę̀±Äel«ËèøL!XîE®öªã)ÓiUß¼\R[Zr<bÖæ¶ëCá½,-J`pÍ:_ÛF÷aZ³ JFÙÜ$éÝ)µe7L¤Ñ1Ìî°ÄÅÆ]¯OÒ"Ò ¨`¨×]Z·ÑÂ#8ÆÂPùdÛØ]"I¢K;j8n<½$f>¿7×ÌEUA0Y#/½åÝm~a·¼þàÉR¨'~ðê"S#µ1ÄÈ2ªñó¹/öX{qÀ?A¼'zö*5;k.¦b¥1Ý¡È ÂEUTd£$=Þ¢§¹%}ƯÁ9¦?k§ekeÓfq3 ý â@6ýÞ äêA"pm!7'³Üõì!賦 qnqa²Þ7ê[x³ ~B°v±¡ÿ¢³|³Áb"²¦«Ç[o ÚcFÄUªoq+³F*.¥ª"ª5AæÄ[;^Þ3¯I{ØäDøÛå4èpçRB k2ÙMT?9ûq$ý~þjny×_2xôSÚ?zd¤÷ª#/ì¾e'i2¥Q eB¹ N¥@Ô#)>)ϳù-ÓàâJ~ùìô{,9ßÔÌT¬¨Ù¤«h(Ô!BÊL*ÔÊ0Ê(C-,0DìIRbÊLÒÍQJSI¨Y¥JE1PR´RQ±¦ÉJh³iRHÙ2ÄÂÌURLLH#Mózi69k/7j2pïж²ÑîX35ulr˱Qüxó9'g¬EáÔÄû®Åu'±-ð4ØR<rO´×1qèJbËõ÷| ÃqMvÁ2ÃmvÜ/ßF2Ýæ H(Éð,9J{Níü!Ù`Bh\ÞpêX¨$ênC1ì>~ïM¤|øxÄÝ<
B¹-`Êü?zÁ¾}MÖ·c/8+ýQ |ÄÒÒ %w ¬k#/L#VL·Ö¡ !b´Å4cí#.ÐøÄÙ¤#/&"#'ÒUû)py7ô.5"#pY1ü#.èsïË£E`ÔLåÅFQÑèìr#fÆðqÒ,b
Þnde0lY¶´.Ò¤x#.#AU>Ûó8.õÝB}=ì×¾WªM#.OnäÀcXù¤K_d0hÆ'ª±ÔÕe§{ÒR¡cH1¤B$&FE`EwqíèkNÇʲÂqaý&X·CmN( Ñ·LÆÖP©f¢Ê4å¡8»;jÿb.V@{zðeÔ·öCh7¥¶DÙ¢bv½:D ÈÛ3)öIQ 4î;&ScuRâàkà$IUSº"××Ýé¯;ÕC Ü'Ã)¾¯A }ð|Í#ö:¿cÙÀ<=¨oZÌéã¯KV}ìÇT;¸nL$Oß9w4¦È©øü!å/ܾ(Ç=0ÜØ9ÔpÙçÊO~U6Y7õbºÓ
½WPsÞVÃ+G¨¼@#`Y)¾½8¯Þñ^mÆêQJ-Õ¼1ë?/Ç]t5*¨¥vÕÐˤé>âàpÓ³1tGyÆËoäb=/POZ8>é§3ñË>7%t¯l±zT\ç0\èb+0 góÏj¾»Ã-D¿_Ô|´äóv}Pô¦~ ø¿[/Ýb¹H4öäÑã<Å,=#.ÊçÄ}wÙæµ~Õ{3ÜÅÌÌz)HI®âÎ^o*ÈoØUbX5{. ÷cÂý\JÜÌ#/±ùwd³CO}XïÐo
Ø#Cý?9nì;£¦·(Q¼0õ0Òc±0ÌÅ4=ÚÐ ¢(Èo?Hõ/mRF_C/ì6&\#)|N)²÷^5òü(ç ">¯1läé#)°üÓE(cD$+ÖLG$`³E#.BÛ<ðÅèçõ¢IÛø!h%UO½=µ.+[ÂgL¯ü.U¨¢]B7A®T&X6£Zs¤Q£n¯Ø¼``Û\[üòk·U*©Ò¦\)T,ÙqQÃÄâ±ÄÎÐ'NW¶-41Ä!¸ 6 NØ2 ¡"DÎlÓSúKJÁhaþR$EF9ȸd\rõÓÇz6^ÈßtÙBBÉÜ4BôBiAG>Ö(įHnç40Hgf]¥¯@Àg^¶pA»f!!¦RĹZq·Ó¡D·nD>ÕNÜH§FaÎ#)Þú]X:ìÐÕe*<L:ÏaúÝh#.%ß0r£NÒ=<Z9«[ØÝWÄ`ÐÓuÇy±Ól¢b+"E¹FÜ72`ÓÆÅÄÈ#/Lj#)ÙÆÖk5X6ëi´lå:N+ûÞO®v²ß[é\. JÃAM*ÅPö¥ÝÑAIZjÝõçv*ü *ºo#)±y-Fár,nÙvì<»Á·ÅüõÀtä¦b^ÓÔ7yÒHpÜ §lT!íYíÕ9ÍÕ¡6.(ÑG$y¡é°JA¬Ù#.ÃI@ôd`å#)o3ZV-!*åS©qÒW>uEaÓáÔÇML{»#.)ª\ãz=7»2¸íÇ'[Ë2eÚöc³K@:6ÛÃ#)ècMB2¤!Ç=Ñ]v`Áë¶"aic&TPõÌè3i´¨CR#);9stB¦Hae°b|ErEbÓ±¬e´è;.Q0éJ£$À§3©2÷k¦LAzîÎ#.iålÓ÷ÍC6Í éÝXܦM× ûA[¤ÙgDØ6@%j%+xVÏ(réUTîåF5Mn¸HÚ?_<c×t!£2u"eñ×1µ»áV^éº6ä)g(XW¹#.FìN¹kµ·\¥»îL`Þj"ì=æ®uV»*6úaSKxQÃtInCÎE?b1&iÓSÕËÅjMë`WËxé°²+ f@!Òõ8
ÔðF¸
¢,;,YBú8ò¾Mt×'plØèr6:Í!4,ÓàTÊ73&ÁÐ9§kégÈkYfhPâë:Ç(:@÷GlÕ§§<é¶m.#T²ÈOøôX¶þòIK+ßqr- ÃÑ?$Cãf#ÈgM6vÕb)(B¢#)ÚÉÃSÓÆ-Î(0
tÌf$áÜtCÉ)xy·AèðãÅ^åÐg¡.PîQÊ¥#.4¨,VO3±ßò\ï:Óð1 ¸ë\Zg;Ø0SC3&3TQUÚBI²pÛ ä¼±j%Í\
u;0e"ÏÎÿðilå3dPå5¥â~>F¯ìoò<4#)ÂL&N¬Õ|îf,E¸C9©jÔÃ2´¦>Fdº¾Ñ²ü¡·îk´õÌØßhæ+,"áÙ¬¸AÁbw°j>yn%[Þ5ÃÊ"0{OCïIõçTbåh"l\D¥!ÈÌN~K´f5½Å¡¶éêÞsEbÑv»û-Z á9hß,§È°ï|('«ñFìÜ0ã2bBÂ0È"ÉJMNKÕÐß©ÃX^ÌY7¥Cf÷G4}@ËDSX:ªÜlÛÄí0á×\µÌæ_ÊȦtê`,Hõç}0jÌÔÁ¿"µÖ$»6qëÛMܹ}#.%Ð$Õ9Ô.poJ
G+§Èb´#.qV= ¼J1Xo ³J©·Á0_£Õäb·N&ñl}i©F"PÈy¡ªhEçAFH"¸ªPÝÔlÙúAÅâ^-µé&Èm#.(.9u =Ó+ÀØáÔ¯ ! ØÐsnúñ%Ha173º1bETcQªdÉ/U!¹È2#.)ª^¥RË]ZO·Cb*Hlik¯°3Là %Ú£B´¢hëC°ø±4uÔ#Æ#.Á9uTUSSx`0G©1 Mc'²õ¸fIRÐv$fé¼lR¥õSgc¨Ç9gc¡ë:*×ÜÄÐi64é:)AÒ"1´|½}¯ËovâHÌ1o_Z4ëãðôWÈtwû{ÉT#ÒÀ$è¦@+âJþßíøõkuP÷ÆÚ}yµÞøØk_¿âLĹ_Lmt¶±þEà1rJo)¬§ÀéglR¤l#.»ÊêinÅxÅØÙ&ªÄÎSÐÄÛ4R¢=míñ¥L3¡m[CÕø¼½£§fÖY?#XAûù£Ê¡ó$_N'ÙÍ4% Ù'1îg:è\?Wæ;£
Ð]Jc²¬©@ñ,TÐiZ ü¬²ýÑ¥÷èáaÝ=¸#.(´,Ùh®
æ¹+©Ü1àT§-ZÊZ÷Áa5_nK!¥^êUq,¼¡ï0}a;]æl©»¬3fcI×=âÿËö/Ùí@ЫóÃyøÆ âwoÙI¥v¸ß<à}RB þöB9î>Úèáp`îP9GhÐÄ8(1²Bd}§p)±¤ç&ðÆcF,ÂE%RÀ!nbhb)Á5¢4æ5.Ö1F!!K(ÆG
#)J d: ´à,c"ºÜ#)°äÐì`a6>#.hÚK(4]mʨ¾+6Û#.ËÁw78ÝR×$¥a#Üûnæ;·òà©ÄáeàÉÐ6Ä31ÁwòiýÆj"W8,èTp RKs1µU º!Êeyð¬ØÎ
N.~I8Ló÷Ú`0]BxÂ:FBuÞÙæÌõhës )ºDßlóë{i*¸ic8x yg8·¢t×N²'<í!ßÝeëû´i³;å4Èdoo=Núñ'Ðv^]nؼáæz=3Õ^lÁA5áÓcØñÈÐHO0s¥1õjÚ²`Ó¥LM¶Ò¿lúãx,¨eõ;O-ÃCpÑî|<g¯åÑø4ú%A fÕe#/YU)o·MdI´EÂ!)àB"jßqÀ#.@ma!úá¡\#)ÒB3¼Eë|¤±T]êBþÚ0ÌÍàÀ0_h¯£qùô¹ÜëáùMòmK2hÕ&C¸û|Þ'ÅN*#.ôÆîóñþH @hA)é÷ïöÛëðÀáñð5_Þ/Zû¼éµóîî?fÜ&HJæHLx!Pm¤B+.e#/(ÅG|Ó8賺UvÞ6®h«¤¸!ADc cL&0
ÁXEw7Á&9#.Êg#.ô!¨#)`ÀìÒ(C jÞþæ`c¯I´éÉ´¶ÆÒ£ùä|?i÷Ðé¨:TÄ8@aªA+XWFÊ$@6m§°{y´1îÚfà`D11#)êã˾ð˾Æ}}E<ø!Ðb!V"á&#.3ºÂ°Ð^¹<xtd37ÇmBÀ(x îr2úÿ.þÝZ¹a#.Ñhäôºòt×¼
gÍ7ß°
l·Íc]s¿"Öv7U0ZT`:Fõ׿³OÃ*²¹ÄZSµäÄÛOGj2EC¡ÎD´Ñ"m8iÇ #/PμÑù,ûÀ¸é,üµðë>=SîìBd~¤x·\"HHmL¦hÌT%ª¶c5¨hÙµ#_u±néI«\gMÓ6»Sk¦Ä-\.ó2Y¾ÆfÁÄæÒórÖ¹ékçí2HÏaM»@#)¯¨alð×6ó½Òd`1#.ÞAãÀèxÌrf;ÃÇÆ`̨fAA0µúîlöï¯}Ý/Ôà&i}#.pÖõYt®HcÉêr½±²m?1ù(Âäq´·¨bwAMÂÍXé΢(4W!ß--¦x£¯êÐJhKwήxCà.[ÎI6`ïRÒS,Ý7èXÖq¹¹¦tØN&6äï¢\7ÎéREYnåɹ¹d£ý~3åÎèxzà0ûU@#)GÍ\pAlj ;LÖÑÆ¥Fà7eÑfCRè·ÛUüÝÉIPË%!OÄ¥AÊgÐÍÛPlö{¼ÐDD½Ç¹kÔz<@ÄHkò(&È#)?Â#/°Ãi«=^u»rÓ³&*k¬Ö§3X ©¬bÌ{HP qTRzýçïC±ãÐ;0ÌûÌu0BN8éHr°ö ¼j#/}þ^äzzà°LLBxzxl=õÅ8V)RÕ\auùPàÖÄ@fR
ï=ã1hw÷ú7ß×¢,L!ó³)²}¿©Èqê.ïäÇú#øÕÑìâÑ·#)íÛºj}dè=àh"Chk?L(¸5ô?··ûÌýfd{AÛ>ø:<¤b'h¿ÝèÐlè$ô÷ô@X4u²ÆBBF$V5È#/·T;¦\U!wRÐüF]Ðkmie1(¡jX¤#.@<ÑhÚdcs]¥ILbSøÀc!õÆ@f'þ"¾ïGz9s$@!»LÄ9mä ÜÁw!2Ï f§ áäGYZE£eOyìA#.ý$´J&D±RjÉfBÉzþ¢Ð¢ bÆm4_^·ñ·y@£´T9~ÅÏGÌnC°FÀü t!Ù#)öcTÄÅòx aÈ5k6Æ*¥¦HHª¢ÛZÒ ,éÀP!/jÿC^" Óýyýe÷?Ãùɵ´\Ŷ¶-óFìõ\zë+7éêÙHCð·©ì( #Ôöäx^#)§6ü¤úÁwŶf´H9Iɧï}#/õÑ>[bF0øíAÄMϸ¹Brd
\òzµÓçX|8h0qà(}f
8dù®Ä/Í1LNI¢®¼LXRM\.MU*Ѳ5¦©Åp ¤Øè미·E7þÂUæ°:C#.Ë1ÃÛRG×8öᬼ×Þã$l-[¢'ÊoÚcÁØÕBÙÕÏÕB>¿Ý/HEùì\¹m/5àÃK#/37â,ú>H°ÅËfÍî- À7Óùÿr¨`{ÀæG·+#/jd¡©M5%ƬdÚ£Yö;[ó-^&°V¥O¦Þ ¬ñnç8bÊpdàÎü1Âåßíé¢4ª¾ ®ü±ºõõF§³g¤¢hJ(h£TR&Úl£Y¤9Ë]}ý.3ß
U9oÕðý[¿l¡©Qûþþ~íù|H¥ß»@¦ô#/:ïPæɪ=õ2ÉôW×ÈnË`¢¬G#.*ó!LÚÓ`4 ÛCGgPFFkþ=#/í"´<¢ #)ܵ¨Æ2Q9ëCE,Ñ*:ûNXn@tïT/HMHìGxç¾I¨s#@Ĺ3·ücAZM±¦ÛcÛ1#)®|ø·#.õ¹¬rÔÂ#/((+SoÝ5êDÑ¥5©;¡Á#.&Ö2Js3 ÚM:hqfeWs¦*7®ís)`! ¥¤u¦ fÚ#.ê¯=uzyåØÝ4¤RX!$YÂ\sË Êd9Êεm!ÄïC¸Ó{Á>¡^á:øÿFê»<=|O¢8c :à¸Âµ'B"ÁSníòvðçvìßä8ù9Âh ;XGDZª±-ÂJ¬3ë8qPàU¯]zqÂf¨þ$-U#(Þ4~%',ÔL'bI~0m/èܱǢF+²¤¥¼¶ú¦É
e²£±\^GÜtwçKæÐq¤F±/SJ¶ý äL¦ÝR¡¾;N ¹ä_<#.Cµß·Xx=;z(©ª?c"ä¡XÝ0íf\kÂ#.ÁpÏ#)äJôÌ«Gà@Å^'"%2ÙPÂàÛÏpðM<.Ûñ#.ÖEy°´:B)clWr<¤Õ±/$Âa#X&ÀwÓà{:|nÿçcWlãꪤ2îz?2P?ó}f1~ûrȤTÚàØ÷#$ ÿ£èy,»móÅr¿4â¯ùøñ=6Çé%Ì¡ý°ªØÆ(Ûv!oÖ#)½=ó«YÑ¥$âIÓmÀLàÅExÁ¨7Cc sÖº;õªdiEàt²jw»l¿Fo3ò ª9%.ºwC¼öBÌ»#.Ný6÷ïk!!×Oé÷óxÜQO3¬Çd!ÔäÎw¢dÍ.ýA6#.P;p-!¯ÛÛ#.VúX7çÒî¥ý!ÔJ!ûxãóà8§+ÝZaH%à÷m7ìýáJw#O$i$#.µüf=múI |ýZz¸pùcm·Ìv3$V:þ¾½5¢ßø¤^É@å £ÿû¿èÿWãÿ§þüßøÿ«á÷í÷ÿïÏÉïÿ³þ¼¿ýÿßÿúü?ãÿ»ÿ/oñÿùÿãnÏû¿íýñÕÿüGü?öõ~ÿöÉÿþÿ¿þüãÿoü¿òÿÏÿó§Ç¯çÿÏããÿ/'ýòìü«ýÿýß/OåýÜïýe¡WúÀýu0 NGötäÌØ?Ò<OéÀÄL¡¹ýÏüb§p_îª":Í>ö»ïy㻦L$Êeõ}7¶#)<ÀéhYéG]¡Ý¦Ñ#.qârB¦¢#)1|ïáüßô`Ò>ÿ«ãÏt¤N=ÁÏ;ó±ëÅÿ3#/ÙnÉéßàÓר7/kLfv̧¾#)c_Ûå5åúûÇûóô¼<<ýÜM*,ɪHÿ xLÑ7AÐÆ@ÊÖÞì?ãp·_GÿË]ç¸Ì+LpÔgR¬Ï÷¸»#)ÞëL*ú²5ìÚÓlkzY(ÁÀïâÄÆlÿgµb¥±tÓ1ì²" «¿¦wpZeX´h=Í0Ñ&5IË7j²î=Ê:2Ãt#EÛ/5£zÚa¦<CêF2UÜFÄ¡®kÙâ°wÌÈ?w&±db:4rø>G7®'?ÕfÀ¾G
ÝÊÄÌY³DÁ¦.^x1«k##½pWÛ!ÑúQÀçfò]d½æ¬ßÝ«¦8±Ï\û·ûÂLGÃð¡ØÕKÛ×{RW_Ç=h¦'b¢:ävvÙ4ÂQªâÙ0Qdæ_i¯wÃpÛYQFîɵÐÓÓ¯.ÏÖc©øi0<X¢1FBß:©Îûs#1ïÎËXÍ¡&Hc|·E!l$zQõ«B$/c^:tmRPL(Qþq±ç³³ð´ªØF%½D«ï&fÿî¸X('ÎñÐ+éÉë:X ÜCæ#/=9À ÛLY¡**#.¨ÖÒ+Y-IjdË1QÏ#1ANùSDlýí!ã ý'üD®Ç4f£¼ô¸PAMècÂØ,1Y>z¨}áåïÏ4ߥ¤üÊËdüæ¾÷å5Ðÿ¿Lcåu¦|àmð5OÏë;á³OVöÊgé·]Æëf~ºWÖ!z ÖÊÞË5»§[3^Á£N#.*cÜAy9·¶Hs¿ÇbËï*ºQHYX .0é¾]qdc"d:,¶¤Ú¾CÞÆÙÊß"Ømþ¿oGÓ;¦¹VÓ£9õ,)$Îò=ܼó¨ÔmË^¼Z´¥¶¼oUxµ&ÅQª!¤¡@ÞéÇZ#.Û!ÛN´e×÷ñàmÒbÄãþìCþsaî&©HÆ.á.òÑUAN6ѧ¨¸ÿã1GTxJ®|*s#/AÇÿûoÉäw×pûmÿ#äzÈ÷ñ<QìGòïÛ¯üàÀgpz~C ÿÑÁ"oZ=¦æ b
#/I)Hs» ©`7Ð{â¡f#)x«åôà{??ãé¬ µs ç\8§;úÕ)6¢ÆÛ&ÚÉ÷É7Üb{??ë°B½?¦^ `ÿÃS3¿ýEÇbËþáÝ«èÿçP½ëåÿ|gøYñ?êßB¿YnýKOü<õxÿ×÷FÏ[ý^¯Á6sìk;ts#©m·¢ òcÛZ=>©(|è'¥A³Ûêæ8¥óTlâs¹ÿêóÑÎmÿ¸¢oívö&j BøÓ6 dJ?C"ϧoÿ5°ü¥kÝqEcõxæþ[¾rÂsóÄ%ÿ©Ä,¨IÔ"Åɲe¾Jíã%S8 új¹åË5ïpCÙõÑ9vÙ8ÊÉN`þçIbðë-bYãhTKY¸Ä!kÁåôéË0"°ÉB7§ÆGBYÈJU3D!×{`&øVW´Ð7-ìÓîÿÑYóâ;×)UEø¿íëzÏÞ"ýß_à`aÿâîH§#/¹)
#<==
diff --git a/wscript b/wscript
index ee2684a..025e481 100644
--- a/wscript
+++ b/wscript
@@ -48,31 +48,27 @@
# vnum = "1.0.0",
features=['cxx', 'cxxshlib'],
source = bld.path.ant_glob(['src/**/*.cpp', 'src/**/*.proto']),
- use = 'BOOST NDN_CXX',
+ use = 'BOOST NDN_CXX LOG4CXX',
includes = ['src', '.'],
+ export_includes=['src', '.'],
)
# Unit tests
if bld.env["_TESTS"]:
bld.recurse('tests')
- if bld.get_define("HAVE_LOG4CXX"):
- libsync.use += ' LOG4CXX'
- if bld.env["_TESTS"]:
- unittests.use += ' LOG4CXX'
-
bld.install_files(
dest = "%s/ChronoSync" % bld.env['INCLUDEDIR'],
- files = bld.path.ant_glob(['src/**/*.h']),
+ files = bld.path.ant_glob(['src/**/*.hpp', 'src/**/*.h', 'common.hpp']),
cwd = bld.path.find_dir("src"),
- relative_trick = True,
+ relative_trick = False,
)
bld.install_files(
dest = "%s/ChronoSync" % bld.env['INCLUDEDIR'],
- files = bld.path.get_bld().ant_glob(['src/**/*.h']),
+ files = bld.path.get_bld().ant_glob(['src/**/*.hpp', 'src/**/*.h', 'common.hpp', 'config.hpp']),
cwd = bld.path.get_bld().find_dir("src"),
- relative_trick = True,
+ relative_trick = False,
)
pc = bld(