<commit_before>/*
 *
 * TezosLikeOriginatedAccount
 *
 * Created by El Khalil Bellakrid on 17/05/2019.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Ledger
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include "TezosLikeOriginatedAccount.h"
#include <database/query/ConditionQueryFilter.h>
#include <api/ErrorCode.hpp>
#include <limits>
#include <wallet/currencies.hpp>
#include <wallet/common/BalanceHistory.hpp>
#include <api/Operation.hpp>
#include <api/OperationOrderKey.hpp>
#include <api/TezosLikeOperation.hpp>
#include <api/TezosLikeTransaction.hpp>
#include <wallet/pool/WalletPool.hpp>

namespace ledger {
    namespace core {
        // we need to compare the block height to the Babylon update height activation
        // because the way balances are computed difffers from pre- and post- Babylon
        // protocol activation
        //
        // <https://tezos.foundation/update-week-of-14-october-2019>
        const int64_t BABYLON_2_0_ACTIVATION_BLOCK_HEIGHT = 655361;

        TezosLikeOriginatedAccount::TezosLikeOriginatedAccount(const std::string &uid,
                                                               const std::string &address,
                                                               const std::shared_ptr<TezosLikeAccount> &originatorAccount,
                                                               bool isSpendable,
                                                               bool isDelegatable,
                                                               const Option<std::string> &publicKey) :
                _accountUid(uid),
                _address(address),
                _originatorAccount(originatorAccount),
                _isSpendable(isSpendable),
                _isDelegatable(isDelegatable),
                _publicKey(publicKey)
        {
        }

        std::string TezosLikeOriginatedAccount::getAccountUid() {
            return _accountUid;
        }

        std::string TezosLikeOriginatedAccount::getAddress() {
            return _address;
        }

        std::experimental::optional<std::string> TezosLikeOriginatedAccount::getPublicKey() {
            return _publicKey.toOptional();
        }

        void TezosLikeOriginatedAccount::getBalance(const std::shared_ptr<api::AmountCallback> & callback) {
            auto localAccount = _originatorAccount.lock();
            if (!localAccount) {
                throw make_exception(api::ErrorCode::NULL_POINTER, "Account was released.");
            }
            getBalance(localAccount->getMainExecutionContext())
                    .callback(localAccount->getMainExecutionContext(), callback);
        }

        FuturePtr<api::Amount> TezosLikeOriginatedAccount::getBalance(const std::shared_ptr<api::ExecutionContext>& context) {
            return std::dynamic_pointer_cast<OperationQuery>(queryOperations()->complete())->execute().mapPtr<api::Amount>(context, [] (const std::vector<std::shared_ptr<api::Operation>> &ops) {
                auto result = BigInt::ZERO;
                for (auto &op : ops) {
                    auto ty = op->getOperationType();
                    auto value = BigInt(op->getAmount()->toString());
                    auto tzOp = op->asTezosLikeOperation();
                    auto tzTx = tzOp->getTransaction();

                    switch (ty) {
                        case api::OperationType::RECEIVE: {
                            // we discard received transactions that have failed
                            if (tzTx->getStatus() == 1) {
                                result = result + value;
                            }

                            break;
                        }

                        case api::OperationType::SEND: {
                            // if sending a transaction has failed:
                            //
                            // - Then we only spend the fees pre- Babylon 2.0.
                            // - We do nothing post- Babylon 2.0 as the fees will be spent by the
                            //   parent account.
                            auto isPreBabylon = op->getBlockHeight().value_or(std::numeric_limits<int64_t>::max()) < BABYLON_2_0_ACTIVATION_BLOCK_HEIGHT;

                            if (tzTx->getStatus() == 0) {
                                value = BigInt::ZERO;
                            }

                            if (isPreBabylon) {
                                auto fees = BigInt(op->getFees()->toString());
                                result = result - (value + fees);
                            } else {
                                // we do not spend the fees, as they are spent by the parent
                                // account
                                result = result - value;
                            }

                            break;
                        }
                        default:
                            break;
                    }
                }

                return std::make_shared<Amount>(currencies::TEZOS, 0, result);
            });
        }

        void TezosLikeOriginatedAccount::getBalanceHistory(const std::chrono::system_clock::time_point & start,
                                                           const std::chrono::system_clock::time_point & end,
                                                           api::TimePeriod period,
                                                           const std::shared_ptr<api::AmountListCallback> & callback) {
            auto localAccount = _originatorAccount.lock();
            if (!localAccount) {
                throw make_exception(api::ErrorCode::NULL_POINTER, "Account was released.");
            }
            getBalanceHistory(localAccount->getMainExecutionContext(), start, end, period)
                    .callback(localAccount->getMainExecutionContext(), callback);
        }

        Future<std::vector<std::shared_ptr<api::Amount>>>
        TezosLikeOriginatedAccount::getBalanceHistory(const std::shared_ptr<api::ExecutionContext>& context,
                                                      const std::chrono::system_clock::time_point & start,
                                                      const std::chrono::system_clock::time_point & end,
                                                      api::TimePeriod period) {
            bool descending = false;
            return std::dynamic_pointer_cast<OperationQuery>(queryOperations()->addOrder(api::OperationOrderKey::DATE, descending)->complete())->execute().map<std::vector<std::shared_ptr<api::Amount>>>(context, [=] (const std::vector<std::shared_ptr<api::Operation>> &ops) {
                struct OperationStrategy {

                    static inline std::chrono::system_clock::time_point date(std::shared_ptr<api::Operation>& op) {
                        return op->getDate();
                    }

                    static inline std::shared_ptr<api::Amount> value_constructor(const BigInt& v) {
                        return std::make_shared<Amount>(currencies::TEZOS, 0, v);
                    }

                    static inline void update_balance(std::shared_ptr<api::Operation>& op, BigInt& sum) {
                        auto value = BigInt(op->getAmount()->toString());
                        auto tzOp = op->asTezosLikeOperation();
                        auto tzTx = tzOp->getTransaction();

                        switch (op->getOperationType()) {
                            case api::OperationType::RECEIVE: {
                                // we discard received transactions that have failed
                                if (tzTx->getStatus() == 1) {
                                  sum = sum + value;
                                }
                                break;
                            }

                            case api::OperationType::SEND: {
                                // if sending a transaction has failed:
                                //
                                // - Then we only spend the fees pre- Babylon 2.0.
                                // - We do nothing post- Babylon 2.0 as the fees will be spent by the
                                //   parent account.
                                auto isPreBabylon = op->getBlockHeight().value_or(std::numeric_limits<int64_t>::max()) < BABYLON_2_0_ACTIVATION_BLOCK_HEIGHT;

                                if (tzTx->getStatus() == 0) {
                                  value = BigInt::ZERO;
                                }

                                if (isPreBabylon) {
                                  auto fees = BigInt(op->getFees()->toString());
                                  sum = sum - (value + fees);
                                } else {
                                  // we do not spend the fees, as they are spent by the parent
                                  // account
                                  sum = sum - value;
                                }

                                break;
                            }

                            default:
                                break;
                        }
                    }
                };

                return agnostic::getBalanceHistoryFor<OperationStrategy, BigInt, api::Amount>(
                        start,
                        end,
                        period,
                        ops.cbegin(),
                        ops.cend(),
                        BigInt::ZERO
                );

            });
        }

        bool TezosLikeOriginatedAccount::isSpendable() {
            return _isSpendable;
        }

        bool TezosLikeOriginatedAccount::isDelegatable() {
            return _isDelegatable;
        }

        std::shared_ptr<api::OperationQuery> TezosLikeOriginatedAccount::queryOperations() {
            auto localAccount = _originatorAccount.lock();
            if (!localAccount) {
                throw make_exception(api::ErrorCode::NULL_POINTER, "Account was released.");
            }
            auto filter = std::make_shared<ConditionQueryFilter<std::string>>("originated_account_uid", "=", _accountUid, "orig_op");
            auto query = std::make_shared<TezosOriginatedOperationQuery>(
                    filter,
                    localAccount->getWallet()->getDatabase(),
                    localAccount->getWallet()->getPool()->getThreadPoolExecutionContext(),
                    localAccount->getWallet()->getMainExecutionContext()
            );
            query->registerAccount(localAccount);
            return query;
        }

        void TezosLikeOriginatedAccount::setPublicKey(const std::string &publicKey) {
            _publicKey = publicKey;
        }

        std::shared_ptr<api::TezosLikeTransactionBuilder> TezosLikeOriginatedAccount::buildTransaction() {
            return _originatorAccount.lock()->buildTransaction(_address);
        }
    }
}
<commit_msg>LLC-639 Move OperationStrategy at namespace level to re-use it in getBalance.<commit_after>/*
 *
 * TezosLikeOriginatedAccount
 *
 * Created by El Khalil Bellakrid on 17/05/2019.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Ledger
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include "TezosLikeOriginatedAccount.h"
#include <database/query/ConditionQueryFilter.h>
#include <api/ErrorCode.hpp>
#include <limits>
#include <wallet/currencies.hpp>
#include <wallet/common/BalanceHistory.hpp>
#include <api/Operation.hpp>
#include <api/OperationOrderKey.hpp>
#include <api/TezosLikeOperation.hpp>
#include <api/TezosLikeTransaction.hpp>
#include <wallet/pool/WalletPool.hpp>

namespace ledger {
    namespace core {
        // we need to compare the block height to the Babylon update height activation
        // because the way balances are computed difffers from pre- and post- Babylon
        // protocol activation
        //
        // <https://tezos.foundation/update-week-of-14-october-2019>
        const int64_t BABYLON_2_0_ACTIVATION_BLOCK_HEIGHT = 655361;

        // type used to compute balances and gather balance history logic; see
        // agnostic::getBalanceHistoryFor for further details
        struct OperationStrategy {
            static inline std::chrono::system_clock::time_point date(std::shared_ptr<api::Operation>& op) {
                return op->getDate();
            }

            static inline std::shared_ptr<api::Amount> value_constructor(const BigInt& v) {
                return std::make_shared<Amount>(currencies::TEZOS, 0, v);
            }

            static inline void update_balance(std::shared_ptr<api::Operation> const& op, BigInt& sum) {
                auto value = BigInt(op->getAmount()->toString());
                auto tzOp = op->asTezosLikeOperation();
                auto tzTx = tzOp->getTransaction();

                switch (op->getOperationType()) {
                    case api::OperationType::RECEIVE: {
                        // we discard received transactions that have failed
                        if (tzTx->getStatus() == 1) {
                          sum = sum + value;
                        }
                        break;
                    }

                    case api::OperationType::SEND: {
                        // if sending a transaction has failed:
                        //
                        // - Then we only spend the fees pre- Babylon 2.0.
                        // - We do nothing post- Babylon 2.0 as the fees will be spent by the
                        //   parent account.
                        auto const isPreBabylon = op->getBlockHeight().value_or(std::numeric_limits<int64_t>::max()) < BABYLON_2_0_ACTIVATION_BLOCK_HEIGHT;

                        if (tzTx->getStatus() == 0) {
                          value = BigInt::ZERO;
                        }

                        if (isPreBabylon) {
                          auto fees = BigInt(op->getFees()->toString());
                          sum = sum - (value + fees);
                        } else {
                          // we do not spend the fees, as they are spent by the parent
                          // account
                          sum = sum - value;
                        }

                        break;
                    }

                    default:
                        break;
                }
            }
        };

        TezosLikeOriginatedAccount::TezosLikeOriginatedAccount(const std::string &uid,
                                                               const std::string &address,
                                                               const std::shared_ptr<TezosLikeAccount> &originatorAccount,
                                                               bool isSpendable,
                                                               bool isDelegatable,
                                                               const Option<std::string> &publicKey) :
                _accountUid(uid),
                _address(address),
                _originatorAccount(originatorAccount),
                _isSpendable(isSpendable),
                _isDelegatable(isDelegatable),
                _publicKey(publicKey)
        {
        }

        std::string TezosLikeOriginatedAccount::getAccountUid() {
            return _accountUid;
        }

        std::string TezosLikeOriginatedAccount::getAddress() {
            return _address;
        }

        std::experimental::optional<std::string> TezosLikeOriginatedAccount::getPublicKey() {
            return _publicKey.toOptional();
        }

        void TezosLikeOriginatedAccount::getBalance(const std::shared_ptr<api::AmountCallback> & callback) {
            auto localAccount = _originatorAccount.lock();
            if (!localAccount) {
                throw make_exception(api::ErrorCode::NULL_POINTER, "Account was released.");
            }
            getBalance(localAccount->getMainExecutionContext())
                    .callback(localAccount->getMainExecutionContext(), callback);
        }

        FuturePtr<api::Amount> TezosLikeOriginatedAccount::getBalance(const std::shared_ptr<api::ExecutionContext>& context) {
            return std::dynamic_pointer_cast<OperationQuery>(queryOperations()->complete())->execute()
                .mapPtr<api::Amount>(context, [] (const std::vector<std::shared_ptr<api::Operation>> &ops) {
                    auto result = BigInt::ZERO;

                    for (auto const &op : ops) {
                        OperationStrategy::update_balance(op, result);
                    }

                    return std::make_shared<Amount>(currencies::TEZOS, 0, result);
            });
        }

        void TezosLikeOriginatedAccount::getBalanceHistory(const std::chrono::system_clock::time_point & start,
                                                           const std::chrono::system_clock::time_point & end,
                                                           api::TimePeriod period,
                                                           const std::shared_ptr<api::AmountListCallback> & callback) {
            auto localAccount = _originatorAccount.lock();
            if (!localAccount) {
                throw make_exception(api::ErrorCode::NULL_POINTER, "Account was released.");
            }
            getBalanceHistory(localAccount->getMainExecutionContext(), start, end, period)
                    .callback(localAccount->getMainExecutionContext(), callback);
        }

        Future<std::vector<std::shared_ptr<api::Amount>>>
        TezosLikeOriginatedAccount::getBalanceHistory(const std::shared_ptr<api::ExecutionContext>& context,
                                                      const std::chrono::system_clock::time_point & start,
                                                      const std::chrono::system_clock::time_point & end,
                                                      api::TimePeriod period) {
            bool descending = false;
            return std::dynamic_pointer_cast<OperationQuery>(queryOperations()->addOrder(api::OperationOrderKey::DATE, descending)->complete())->execute().map<std::vector<std::shared_ptr<api::Amount>>>(context, [=] (const std::vector<std::shared_ptr<api::Operation>> &ops) {
                return agnostic::getBalanceHistoryFor<OperationStrategy, BigInt, api::Amount>(
                        start,
                        end,
                        period,
                        ops.cbegin(),
                        ops.cend(),
                        BigInt::ZERO
                );

            });
        }

        bool TezosLikeOriginatedAccount::isSpendable() {
            return _isSpendable;
        }

        bool TezosLikeOriginatedAccount::isDelegatable() {
            return _isDelegatable;
        }

        std::shared_ptr<api::OperationQuery> TezosLikeOriginatedAccount::queryOperations() {
            auto localAccount = _originatorAccount.lock();
            if (!localAccount) {
                throw make_exception(api::ErrorCode::NULL_POINTER, "Account was released.");
            }
            auto filter = std::make_shared<ConditionQueryFilter<std::string>>("originated_account_uid", "=", _accountUid, "orig_op");
            auto query = std::make_shared<TezosOriginatedOperationQuery>(
                    filter,
                    localAccount->getWallet()->getDatabase(),
                    localAccount->getWallet()->getPool()->getThreadPoolExecutionContext(),
                    localAccount->getWallet()->getMainExecutionContext()
            );
            query->registerAccount(localAccount);
            return query;
        }

        void TezosLikeOriginatedAccount::setPublicKey(const std::string &publicKey) {
            _publicKey = publicKey;
        }

        std::shared_ptr<api::TezosLikeTransactionBuilder> TezosLikeOriginatedAccount::buildTransaction() {
            return _originatorAccount.lock()->buildTransaction(_address);
        }
    }
}
<|endoftext|>


<commit_before>#ifndef _SDD_HOM_LOCAL_HH_
#define _SDD_HOM_LOCAL_HH_

#include <iosfwd>

#include "sdd/dd/definition.hh"
#include "sdd/hom/context_fwd.hh"
#include "sdd/hom/definition_fwd.hh"
#include "sdd/hom/evaluation_error.hh"
#include "sdd/hom/identity.hh"
#include "sdd/order/order.hh"

namespace sdd { namespace hom {

/*-------------------------------------------------------------------------------------------*/

/// @cond INTERNAL_DOC

/// @brief Local homomorphism.
template <typename C>
class local
{
private:

  /// @brief The variable type.
  typedef typename C::Variable variable_type;

  /// @brief The identifier type.
  typedef typename C::Identifier identifier_type;

  /// @brief The hierarchical node where the nested homomorphism will be carried to.
  const variable_type variable_;

  ///
  const identifier_type identifier_;

  /// @brief The nested homomorphism to apply in a nested level.
  const homomorphism<C> h_;

public:

  /// @brief Constructor.
  local(const identifier_type& id, const order::order<C>& o, const homomorphism<C>& h)
    : variable_(o.identifier_variable(id))
    , identifier_(id)
    , h_(h)
  {
  }

  /// @brief Local's evaluation implementation.
  struct evaluation
  {
    typedef SDD<C> result_type;

    SDD<C>
    operator()( const hierarchical_node<C>& n
              , context<C>& cxt, const order::order<C>& o, const variable_type& var
              , const homomorphism<C>& h, const SDD<C>& s)
    const
    {
      if (h.selector()) // partition won't change
      {
        square_union<C, SDD<C>> su;
        su.reserve(node.size());
        for (const auto& arc : node)
        {
          SDD<C> new_valuation = h(cxt, o.nested(), arc.valuation());
          if (not new_valuation.empty())
          {
            su.add(arc.successor(), new_valuation);
          }
        }
        return SDD<C>(node.variable(), su(cxt.sdd_context()));
      }
      else
      {
        sum_builder<C, SDD<C>> sum_operands(node.size());
        for (const auto& arc : node)
        {
          sum_operands.add(SDD<C>(var, h(cxt, o.nested(), arc.valuation()), arc.successor()));
        }

        try
        {
          return sdd::sum(cxt.sdd_context(), std::move(sum_operands));
        }
        catch (top<C>& t)
        {
          evaluation_error<C> e(s);
          e.add_top(t);
          throw e;
        }
      }
    }

    template <typename T>
    SDD<C>
    operator()( const T&, context<C>&, const order::order<C>&, const variable_type&
              , const homomorphism<C>&, const SDD<C> s)
    const
    {
      throw evaluation_error<C>(s);
    }
  };

  /// @brief Evaluation.
  SDD<C>
  operator()(context<C>& cxt, const order::order<C>& o, const SDD<C>& s)
  const
  {
    return apply_visitor(evaluation(), s->data(), cxt, o, variable_, h_, s);
  }

  /// @brief Skip predicate.
  bool
  skip(const order::order<C>& o)
  const noexcept
  {
    return o.variable() != variable_;
  }

  /// @brief Selector predicate
  bool
  selector()
  const noexcept
  {
    return h_.selector();
  }

  /// @brief Return the target.
  const variable_type&
  variable()
  const noexcept
  {
    return variable_;
  }

  /// @brief Return the target.
  const identifier_type&
  identifier()
  const noexcept
  {
    return identifier_;
  }

  /// @brief Return the carried homomorphism.
  homomorphism<C>
  hom()
  const noexcept
  {
    return h_;
  }
};

/*-------------------------------------------------------------------------------------------*/

/// @brief Equality of two Local homomorphisms.
/// @related local
template <typename C>
inline
bool
operator==(const local<C>& lhs, const local<C>& rhs)
noexcept
{
  return lhs.variable() == rhs.variable() and lhs.hom() == rhs.hom();
}

/// @related local
template <typename C>
std::ostream&
operator<<(std::ostream& os, const local<C>& l)
{
  return os << "@(" << l.variable() << ", " << l.hom() << ")";
}

/// @endcond

/*-------------------------------------------------------------------------------------------*/

/// @brief Create the Local homomorphism.
/// @related homomorphism
template <typename C>
homomorphism<C>
Local(const typename C::Identifier& id, const order::order<C>& o, const homomorphism<C>& h)
{
  if (h == Id<C>())
  {
    return h;
  }
  else
  {
    return homomorphism<C>::create(internal::mem::construct<local<C>>(), id, o, h);
  }
}

/*-------------------------------------------------------------------------------------------*/

}} // namespace sdd::hom

namespace std {

/*-------------------------------------------------------------------------------------------*/

/// @cond INTERNAL_DOC

/// @brief Hash specialization for sdd::hom::local.
template <typename C>
struct hash<sdd::hom::local<C>>
{
  std::size_t
  operator()(const sdd::hom::local<C>& l)
  const noexcept
  {
    std::size_t seed = 0;
    sdd::internal::util::hash_combine(seed, l.variable());
    sdd::internal::util::hash_combine(seed, l.hom());
    return seed;
  }
};

/// @endcond

/*-------------------------------------------------------------------------------------------*/

} // namespace std

#endif // _SDD_HOM_FIXPOINT_HH_
<commit_msg>Don't compile. Correct parameter name.<commit_after>#ifndef _SDD_HOM_LOCAL_HH_
#define _SDD_HOM_LOCAL_HH_

#include <iosfwd>

#include "sdd/dd/definition.hh"
#include "sdd/hom/context_fwd.hh"
#include "sdd/hom/definition_fwd.hh"
#include "sdd/hom/evaluation_error.hh"
#include "sdd/hom/identity.hh"
#include "sdd/order/order.hh"

namespace sdd { namespace hom {

/*-------------------------------------------------------------------------------------------*/

/// @cond INTERNAL_DOC

/// @brief Local homomorphism.
template <typename C>
class local
{
private:

  /// @brief The variable type.
  typedef typename C::Variable variable_type;

  /// @brief The identifier type.
  typedef typename C::Identifier identifier_type;

  /// @brief The hierarchical node where the nested homomorphism will be carried to.
  const variable_type variable_;

  ///
  const identifier_type identifier_;

  /// @brief The nested homomorphism to apply in a nested level.
  const homomorphism<C> h_;

public:

  /// @brief Constructor.
  local(const identifier_type& id, const order::order<C>& o, const homomorphism<C>& h)
    : variable_(o.identifier_variable(id))
    , identifier_(id)
    , h_(h)
  {
  }

  /// @brief Local's evaluation implementation.
  struct evaluation
  {
    typedef SDD<C> result_type;

    SDD<C>
    operator()( const hierarchical_node<C>& node
              , context<C>& cxt, const order::order<C>& o, const variable_type& var
              , const homomorphism<C>& h, const SDD<C>& s)
    const
    {
      if (h.selector()) // partition won't change
      {
        square_union<C, SDD<C>> su;
        su.reserve(node.size());
        for (const auto& arc : node)
        {
          SDD<C> new_valuation = h(cxt, o.nested(), arc.valuation());
          if (not new_valuation.empty())
          {
            su.add(arc.successor(), new_valuation);
          }
        }
        return SDD<C>(node.variable(), su(cxt.sdd_context()));
      }
      else
      {
        sum_builder<C, SDD<C>> sum_operands(node.size());
        for (const auto& arc : node)
        {
          sum_operands.add(SDD<C>(var, h(cxt, o.nested(), arc.valuation()), arc.successor()));
        }

        try
        {
          return sdd::sum(cxt.sdd_context(), std::move(sum_operands));
        }
        catch (top<C>& t)
        {
          evaluation_error<C> e(s);
          e.add_top(t);
          throw e;
        }
      }
    }

    template <typename T>
    SDD<C>
    operator()( const T&, context<C>&, const order::order<C>&, const variable_type&
              , const homomorphism<C>&, const SDD<C> s)
    const
    {
      throw evaluation_error<C>(s);
    }
  };

  /// @brief Evaluation.
  SDD<C>
  operator()(context<C>& cxt, const order::order<C>& o, const SDD<C>& s)
  const
  {
    return apply_visitor(evaluation(), s->data(), cxt, o, variable_, h_, s);
  }

  /// @brief Skip predicate.
  bool
  skip(const order::order<C>& o)
  const noexcept
  {
    return o.variable() != variable_;
  }

  /// @brief Selector predicate
  bool
  selector()
  const noexcept
  {
    return h_.selector();
  }

  /// @brief Return the target.
  const variable_type&
  variable()
  const noexcept
  {
    return variable_;
  }

  /// @brief Return the target.
  const identifier_type&
  identifier()
  const noexcept
  {
    return identifier_;
  }

  /// @brief Return the carried homomorphism.
  homomorphism<C>
  hom()
  const noexcept
  {
    return h_;
  }
};

/*-------------------------------------------------------------------------------------------*/

/// @brief Equality of two Local homomorphisms.
/// @related local
template <typename C>
inline
bool
operator==(const local<C>& lhs, const local<C>& rhs)
noexcept
{
  return lhs.variable() == rhs.variable() and lhs.hom() == rhs.hom();
}

/// @related local
template <typename C>
std::ostream&
operator<<(std::ostream& os, const local<C>& l)
{
  return os << "@(" << l.variable() << ", " << l.hom() << ")";
}

/// @endcond

/*-------------------------------------------------------------------------------------------*/

/// @brief Create the Local homomorphism.
/// @related homomorphism
template <typename C>
homomorphism<C>
Local(const typename C::Identifier& id, const order::order<C>& o, const homomorphism<C>& h)
{
  if (h == Id<C>())
  {
    return h;
  }
  else
  {
    return homomorphism<C>::create(internal::mem::construct<local<C>>(), id, o, h);
  }
}

/*-------------------------------------------------------------------------------------------*/

}} // namespace sdd::hom

namespace std {

/*-------------------------------------------------------------------------------------------*/

/// @cond INTERNAL_DOC

/// @brief Hash specialization for sdd::hom::local.
template <typename C>
struct hash<sdd::hom::local<C>>
{
  std::size_t
  operator()(const sdd::hom::local<C>& l)
  const noexcept
  {
    std::size_t seed = 0;
    sdd::internal::util::hash_combine(seed, l.variable());
    sdd::internal::util::hash_combine(seed, l.hom());
    return seed;
  }
};

/// @endcond

/*-------------------------------------------------------------------------------------------*/

} // namespace std

#endif // _SDD_HOM_FIXPOINT_HH_
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"

#ifndef _PreComp_
# include <QApplication>
# include <QModelIndex>
# include <QPainter>
#endif

#include "PropertyItemDelegate.h"
#include "PropertyItem.h"

using namespace Gui::PropertyEditor;

PropertyItemDelegate::PropertyItemDelegate(QObject* parent)
    : QItemDelegate(parent), pressed(false)
{
}

PropertyItemDelegate::~PropertyItemDelegate()
{
}

QSize PropertyItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
{
    QSize size = QItemDelegate::sizeHint(option, index);
    size.setHeight(20);
    return size;
}

void PropertyItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const
{
    QStyleOptionViewItem option = opt;

    PropertyItem *property = static_cast<PropertyItem*>(index.internalPointer());

    if (property && property->isSeparator()) {
        option.palette.setColor(QPalette::Text, option.palette.color(QPalette::BrightText));
        option.font.setBold(true);
        option.state &= ~QStyle::State_Selected;
    }

    if (index.column() == 1) {
        option.state &= ~QStyle::State_Selected;
    }

    option.state &= ~QStyle::State_HasFocus;

    if (property && property->isSeparator()) {
        QBrush bg = option.palette.dark();
        painter->fillRect(option.rect, bg);
    }

    QPen savedPen = painter->pen();

    QItemDelegate::paint(painter, option, index);

    QColor color = static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &option));
    painter->setPen(QPen(color));
    if (index.column() == 1 || !(property && property->isSeparator())) {
        int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left();
        painter->drawLine(right, option.rect.y(), right, option.rect.bottom());
    }
    painter->drawLine(option.rect.x(), option.rect.bottom(),
            option.rect.right(), option.rect.bottom());
    painter->setPen(savedPen);
}

bool PropertyItemDelegate::editorEvent (QEvent * event, QAbstractItemModel* model,
                                        const QStyleOptionViewItem& option, const QModelIndex& index)
{
    if (event && event->type() == QEvent::MouseButtonPress)
        this->pressed = true;
    else
        this->pressed = false;
    return QItemDelegate::editorEvent(event, model, option, index);
}

QWidget * PropertyItemDelegate::createEditor (QWidget * parent, const QStyleOptionViewItem & /*option*/,
                                              const QModelIndex & index ) const
{
    if (!index.isValid())
        return 0;

    PropertyItem *childItem = static_cast<PropertyItem*>(index.internalPointer());
    if (!childItem)
        return 0;
    QWidget* editor = childItem->createEditor(parent, this, SLOT(valueChanged()));
    if (editor && childItem->isReadOnly())
        editor->setDisabled(true);
    else if (editor && this->pressed)
        editor->setFocus();
    this->pressed = false;
    return editor;
}

void PropertyItemDelegate::valueChanged()
{
    QWidget* editor = qobject_cast<QWidget*>(sender());
    if (editor)
        commitData(editor);
}

void PropertyItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    if (!index.isValid())
        return;
    QVariant data = index.data(Qt::EditRole);
    PropertyItem *childItem = static_cast<PropertyItem*>(index.internalPointer());
    editor->blockSignals(true);
    childItem->setEditorData(editor, data);
    editor->blockSignals(false);
    return;
}

void PropertyItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
    if (!index.isValid())
        return;
    PropertyItem *childItem = static_cast<PropertyItem*>(index.internalPointer());
    QVariant data = childItem->editorData(editor);
    model->setData(index, data, Qt::EditRole);
}

#include "moc_PropertyItemDelegate.cpp"
<commit_msg>+ don't use hardcoded height in PropertyItemDelegate::sizeHint<commit_after>/***************************************************************************
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"

#ifndef _PreComp_
# include <QApplication>
# include <QModelIndex>
# include <QPainter>
#endif

#include "PropertyItemDelegate.h"
#include "PropertyItem.h"

using namespace Gui::PropertyEditor;

PropertyItemDelegate::PropertyItemDelegate(QObject* parent)
    : QItemDelegate(parent), pressed(false)
{
}

PropertyItemDelegate::~PropertyItemDelegate()
{
}

QSize PropertyItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
{
    QSize size = QItemDelegate::sizeHint(option, index);
    size += QSize(0, 5);
    return size;
}

void PropertyItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const
{
    QStyleOptionViewItem option = opt;

    PropertyItem *property = static_cast<PropertyItem*>(index.internalPointer());

    if (property && property->isSeparator()) {
        option.palette.setColor(QPalette::Text, option.palette.color(QPalette::BrightText));
        option.font.setBold(true);
        option.state &= ~QStyle::State_Selected;
    }

    if (index.column() == 1) {
        option.state &= ~QStyle::State_Selected;
    }

    option.state &= ~QStyle::State_HasFocus;

    if (property && property->isSeparator()) {
        QBrush bg = option.palette.dark();
        painter->fillRect(option.rect, bg);
    }

    QPen savedPen = painter->pen();

    QItemDelegate::paint(painter, option, index);

    QColor color = static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &option));
    painter->setPen(QPen(color));
    if (index.column() == 1 || !(property && property->isSeparator())) {
        int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left();
        painter->drawLine(right, option.rect.y(), right, option.rect.bottom());
    }
    painter->drawLine(option.rect.x(), option.rect.bottom(),
            option.rect.right(), option.rect.bottom());
    painter->setPen(savedPen);
}

bool PropertyItemDelegate::editorEvent (QEvent * event, QAbstractItemModel* model,
                                        const QStyleOptionViewItem& option, const QModelIndex& index)
{
    if (event && event->type() == QEvent::MouseButtonPress)
        this->pressed = true;
    else
        this->pressed = false;
    return QItemDelegate::editorEvent(event, model, option, index);
}

QWidget * PropertyItemDelegate::createEditor (QWidget * parent, const QStyleOptionViewItem & /*option*/,
                                              const QModelIndex & index ) const
{
    if (!index.isValid())
        return 0;

    PropertyItem *childItem = static_cast<PropertyItem*>(index.internalPointer());
    if (!childItem)
        return 0;
    QWidget* editor = childItem->createEditor(parent, this, SLOT(valueChanged()));
    if (editor && childItem->isReadOnly())
        editor->setDisabled(true);
    else if (editor && this->pressed)
        editor->setFocus();
    this->pressed = false;
    return editor;
}

void PropertyItemDelegate::valueChanged()
{
    QWidget* editor = qobject_cast<QWidget*>(sender());
    if (editor)
        commitData(editor);
}

void PropertyItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    if (!index.isValid())
        return;
    QVariant data = index.data(Qt::EditRole);
    PropertyItem *childItem = static_cast<PropertyItem*>(index.internalPointer());
    editor->blockSignals(true);
    childItem->setEditorData(editor, data);
    editor->blockSignals(false);
    return;
}

void PropertyItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
    if (!index.isValid())
        return;
    PropertyItem *childItem = static_cast<PropertyItem*>(index.internalPointer());
    QVariant data = childItem->editorData(editor);
    model->setData(index, data, Qt::EditRole);
}

#include "moc_PropertyItemDelegate.cpp"
<|endoftext|>


<commit_before>/*
 * Common.hpp
 *
 * Created on: 3 maj 2014
 * Author: Lech Kulina
 * E-Mail: kulinalech@gmail.com
 */

#ifndef COMMON_HPP_
#define COMMON_HPP_

namespace Cosmic {

namespace Physics {

    const float PI  = 3.141592653589793;

    template<class T>
    inline T degrees(const T& radians) {
        return (radians * 180) / PI;
    }

    template<class T>
    inline T radians(const T& degrees) {
        return (PI * degrees) / 180;
    }

}

}

#endif /* COMMON_HPP_ */
<commit_msg>Fix header guards in physics common header<commit_after>/*
 * Common.hpp
 *
 * Created on: 3 maj 2014
 * Author: Lech Kulina
 * E-Mail: kulinalech@gmail.com
 */

#ifndef COSMIC_PHYSICS_COMMON_HPP_
#define COSMIC_PHYSICS_COMMON_HPP_

namespace Cosmic {

namespace Physics {

    const float PI  = 3.141592653589793;

    template<class T>
    inline T degrees(const T& radians) {
        return (radians * 180) / PI;
    }

    template<class T>
    inline T radians(const T& degrees) {
        return (PI * degrees) / 180;
    }

}

}

#endif /* COSMIC_PHYSICS_COMMON_HPP_ */
<|endoftext|>


<commit_before>#include <AccumulationBuffer.h>
#include <Sampler.h>
#include <hayai.hpp>

namespace AccumulationBufferBenchmark {

    class AccumulationBufferBenchmark : public ::hayai::Fixture {
    public:
        virtual void SetUp() {

            for (int i = 0; i < toAccumulate.width * toAccumulate.height; ++i) {
                toAccumulate[i] = Vector3(sampler.genrand_real2(), sampler.genrand_real2(), sampler.genrand_real2());
            }
        }

        virtual void TearDown() {

        }

        Sampler sampler = Sampler(13499);
        AccumulationBuffer accBuff = AccumulationBuffer(1024, 1024);
        Image toAccumulate = Image(1024, 1024);
    };

    BENCHMARK_F(AccumulationBufferBenchmark, accumulate, 10, 100) {
        accBuff += toAccumulate;
    }

}
<commit_msg>Adapts benchmark iterations to match JMH sibling<commit_after>#include <AccumulationBuffer.h>
#include <Sampler.h>
#include <hayai.hpp>

namespace AccumulationBufferBenchmark {

    class AccumulationBufferBenchmark : public ::hayai::Fixture {
    public:
        virtual void SetUp() {

            for (int i = 0; i < toAccumulate.width * toAccumulate.height; ++i) {
                toAccumulate[i] = Vector3(sampler.genrand_real2(), sampler.genrand_real2(), sampler.genrand_real2());
            }
        }

        virtual void TearDown() {

        }

        Sampler sampler = Sampler(13499);
        AccumulationBuffer accBuff = AccumulationBuffer(1024, 1024);
        Image toAccumulate = Image(1024, 1024);
    };

    BENCHMARK_F(AccumulationBufferBenchmark, accumulate, 100, 1) {
        accBuff += toAccumulate;
    }

}
<|endoftext|>


<commit_before>#include <bits/stdc++.h>

using namespace std;

typedef pair<long long, long long> point;

long long cross(const point &a, const point &b, const point &c) {
    return (b.first - a.first) * (c.second - a.second) - (b.second - a.second) * (c.first - a.first);
}

vector<point> convex_hull(vector<point> points) {
    if (points.size() <= 1)
        return points;
    sort(points.begin(), points.end());
    vector<point> h;
    for (auto &p: points) {
        while (h.size() >= 2 && cross(h.end()[-2], h.back(), p) >= 0)
            h.pop_back();
        h.emplace_back(p);
    }
    reverse(points.begin(), points.end());
    int upper = h.size();
    for (auto &p: points) {
        while (h.size() > upper && cross(h.end()[-2], h.back(), p) >= 0)
            h.pop_back();
        h.emplace_back(p);
    }
    h.resize(h.size() - 1 - (h[0] == h[1]));
    return h;
}

// usage example
int main() {
    vector<point> hull1 = convex_hull({{0, 0},
                                       {3, 0},
                                       {0, 3},
                                       {1, 1}});
    cout << (3 == hull1.size()) << endl;

    vector<point> hull2 = convex_hull({{0, 0},
                                       {0, 0}});
    cout << (1 == hull2.size()) << endl;
}
<commit_msg>update<commit_after>#include <bits/stdc++.h>

using namespace std;

typedef pair<long long, long long> point;

long long cross(const point &a, const point &b, const point &c) {
    return (b.first - a.first) * (c.second - a.second) - (b.second - a.second) * (c.first - a.first);
}

// https://en.wikipedia.org/wiki/Convex_hull in O(n*log(n))
vector<point> convex_hull(vector<point> points) {
    if (points.size() <= 1)
        return points;
    sort(points.begin(), points.end());
    vector<point> h;
    for (auto &p: points) {
        while (h.size() >= 2 && cross(h.end()[-2], h.back(), p) >= 0)
            h.pop_back();
        h.emplace_back(p);
    }
    reverse(points.begin(), points.end());
    int upper = h.size();
    for (auto &p: points) {
        while (h.size() > upper && cross(h.end()[-2], h.back(), p) >= 0)
            h.pop_back();
        h.emplace_back(p);
    }
    h.resize(h.size() - 1 - (h[0] == h[1]));
    return h;
}

// usage example
int main() {
    vector<point> hull1 = convex_hull({{0, 0},
                                       {3, 0},
                                       {0, 3},
                                       {1, 1}});
    cout << (3 == hull1.size()) << endl;

    vector<point> hull2 = convex_hull({{0, 0},
                                       {0, 0}});
    cout << (1 == hull2.size()) << endl;
}
<|endoftext|>


<commit_before>/** \file   FullTextCache.cc
 *  \brief  Implementation of functions relating to our full-text cache.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2017-2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "FullTextCache.h"
#include <algorithm>
#include <ctime>
#include <sstream>
#include <tuple>
#include "Compiler.h"
#include "DbRow.h"
#include "FileUtil.h"
#include "PdfUtil.h"
#include "Random.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "TimeUtil.h"
#include "UrlUtil.h"
#include "util.h"
#include "VuFind.h"

#include <iostream>

constexpr unsigned MIN_CACHE_EXPIRE_TIME_ON_ERROR(42300 * 60); // About 1 month in seconds.
constexpr unsigned MAX_CACHE_EXPIRE_TIME_ON_ERROR(42300 * 60 * 2); // About 2 months in seconds.

static const std::map<std::string, FullTextCache::TextType> description_to_text_type_map {
    { "Volltext", FullTextCache::FULLTEXT },
    { "Inhaltsverzeichnis", FullTextCache::TOC },
    { "Inhaltstext", FullTextCache::ABSTRACT },
    { "Zusammenfassung", FullTextCache::SUMMARY }
};

bool FullTextCache::getDomainFromUrl(const std::string &url, std::string * const domain) const {
    std::string scheme, username_password, authority, port, path, params, query, fragment, relative_url;
    const bool result(UrlUtil::ParseUrl(url, &scheme, &username_password, &authority, &port, &path, &params, &query,
                                        &fragment, &relative_url));
    if (result)
        *domain = authority;

    return result;
}

bool FullTextCache::entryExpired(const std::string &id, std::vector<std::string> urls) {
    Entry entry;
    if (not getEntry(id, &entry))
        return true;
    const time_t now(std::time(nullptr));
    if (entry.expiration_ == TimeUtil::BAD_TIME_T or now < entry.expiration_) {
        std::vector<std::string> existing_urls(FullTextCache::getEntryUrlsAsStrings(id));

        std::sort(existing_urls.begin(), existing_urls.end());
        std::sort(urls.begin(), urls.end());
        if (urls == existing_urls)
            return false;
    }

    return true;
}

bool FullTextCache::singleUrlExpired(const std::string &id, const std::string &url) {
    Entry entry;
    if (not getEntry(id, &entry))
        return true;
    if (entry.expiration_ == TimeUtil::BAD_TIME_T or (std::time(nullptr) < entry.expiration_)) {
        for (const auto &entry_url : FullTextCache::getEntryUrls(id)) {
            if (entry_url.url_ == url and entry_url.error_message_.empty())
                return false;
        }
    }
    return true;
}

bool FullTextCache::dummyEntryExists(const std::string &id) {
    Entry entry;
    if (not getEntry(id, &entry))
        return false;
    for (const auto &entry_url : FullTextCache::getEntryUrls(id)) {
        if (entry_url.url_ == DUMMY_URL)
            return true;
    }
    return false;
}

void FullTextCache::expireEntries() {
    full_text_cache_urls_.deleteRange("expiration", Elasticsearch::RO_LTE, "now");
}

bool FullTextCache::getEntry(const std::string &id, Entry * const entry) const {
    const auto results(full_text_cache_.simpleSelect({ "expiration" }, "id", id));
    if (results.empty())
        return false;

    entry->id_ = id;
    if (results.front().find("expiration") == results.front().cend())
        entry->expiration_ = TimeUtil::BAD_TIME_T;
    else
        entry->expiration_ = TimeUtil::Iso8601StringToTimeT(results.front().find("expiration")->second);
    return true;
}

inline std::string GetValueOrEmptyString(const std::map<std::string, std::string> &map, const std::string &key) {
    const auto pair(map.find(key));
    return pair == map.cend() ? "" : pair->second;
}

std::vector<FullTextCache::EntryUrl> FullTextCache::getEntryUrls(const std::string &id) const {
    const auto results(full_text_cache_urls_.simpleSelect({ "url", "domain", "error_message" }, "id", id));

    std::vector<EntryUrl> entry_urls;
    entry_urls.reserve(results.size());
    for (const auto &map : results) {
        EntryUrl entry_url;
        entry_url.id_            = id;
        entry_url.url_           = GetValueOrEmptyString(map, "url");
        entry_url.domain_        = GetValueOrEmptyString(map, "domain");
        entry_url.error_message_ = GetValueOrEmptyString(map, "error_message");
        entry_urls.emplace_back(entry_url);
    }
    return entry_urls;
}

std::vector<std::string> FullTextCache::getEntryUrlsAsStrings(const std::string &id) const {
    const auto results(full_text_cache_urls_.simpleSelect({ "url" }, "id", id));

    std::vector<std::string> urls;
    for (const auto &map : results) {

        const auto url(GetValueOrEmptyString(map, "url"));
        if (not url.empty())
            urls.emplace_back(url);
    }
    return urls;
}

unsigned FullTextCache::getErrorCount() const {
    return full_text_cache_urls_.count({ { "error_message", "*" } });
}

bool FullTextCache::getFullText(const std::string &id, std::string * const full_text) const {
    const auto results(full_text_cache_.simpleSelect({ "full_text" }, "id", id));

    if (results.empty())
        return false;

    *full_text = GetValueOrEmptyString(results.front(), "full_text");
    return true;
}

const std::string US("\x1F"); // ASCII unit separator

std::vector<FullTextCache::EntryGroup> FullTextCache::getEntryGroupsByDomainAndErrorMessage() const {
    const auto results(full_text_cache_urls_.simpleSelect({ "url", "domain", "error_message", "id" }));

    std::unordered_map<std::string, std::tuple<std::string, std::string, unsigned>> domains_and_errors_to_ids_and_urls_and_counts_map;
    for (const auto &map : results) {
        const auto url_pair(map.find("url"));
        const auto domain_pair(map.find("domain"));
        const auto error_message_pair(map.find("error_message"));
        if (url_pair == map.cend() or domain_pair == map.cend() or error_message_pair == map.cend())
            continue;

        const auto id_pair(map.find("id"));
        const auto key(domain_pair->second + US + error_message_pair->second);
        auto id_and_url_and_count(domains_and_errors_to_ids_and_urls_and_counts_map.find(key));
        if (id_and_url_and_count != domains_and_errors_to_ids_and_urls_and_counts_map.end())
            ++std::get<2>(id_and_url_and_count->second);
        else
            domains_and_errors_to_ids_and_urls_and_counts_map[key] = std::make_tuple(id_pair->second, url_pair->second, 1);
    }

    std::vector<EntryGroup> groups;
    groups.reserve(domains_and_errors_to_ids_and_urls_and_counts_map.size());
    for (const auto &domain_and_error_to_id_and_url_and_count : domains_and_errors_to_ids_and_urls_and_counts_map) {
        std::vector<std::string> parts;
        if (unlikely(StringUtil::Split(domain_and_error_to_id_and_url_and_count.first, US, &parts) != 2))
            LOG_ERROR("This should never happen (" + std::to_string(parts.size()) + "): "
                      + StringUtil::CStyleEscape(domain_and_error_to_id_and_url_and_count.first));

        groups.emplace_back(EntryGroup(std::get<2>(domain_and_error_to_id_and_url_and_count.second),
                                       parts[0], parts[1],
                                       std::get<0>(domain_and_error_to_id_and_url_and_count.second),
                                       std::get<1>(domain_and_error_to_id_and_url_and_count.second)));
    }

    std::sort(groups.begin(), groups.end(), [](const EntryGroup &eg1, const EntryGroup &eg2){ return eg1.count_ > eg2.count_;});
    return groups;
}

std::vector<FullTextCache::EntryUrl> FullTextCache::getJoinedEntriesByDomainAndErrorMessage(const std::string &domain_,
                                                                                const std::string &error_message_) const
{
    const auto results(full_text_cache_urls_.simpleSelect({ "url", "domain", "error_message", "id" },
                                                          { { "domain", domain_ }, { "error_message", error_message_ } }));

    std::vector<EntryUrl> entries;
    for (const auto &map : results) {
        const std::string url(GetValueOrEmptyString(map, "url"));
        const std::string domain(GetValueOrEmptyString(map, "domain"));
        const std::string error_message(GetValueOrEmptyString(map, "error_message"));
        const std::string id(GetValueOrEmptyString(map, "id"));

        entries.emplace_back(EntryUrl(id, url, domain, error_message));
    }

    std::sort(entries.begin(), entries.end(), [](const EntryUrl &eu1, const EntryUrl &eu2){ return eu1.id_ < eu2.id_; });
    return entries;
}

FullTextCache::EntryUrl FullTextCache::getJoinedEntryByDomainAndErrorMessage(const std::string &domain_, const std::string &error_message_)
    const
{
    const auto results(full_text_cache_urls_.simpleSelect({ "url", "domain", "error_message", "id" },
                                                          { { "domain", domain_ }, { "error_message", error_message_ } },
                                                          /* max_count = */1));
    if (unlikely(results.size() != 1))
        LOG_ERROR("failed to get one entry!");

    const std::string url(GetValueOrEmptyString(results.front(), "url"));
    const std::string domain(GetValueOrEmptyString(results.front(), "domain"));
    const std::string error_message(GetValueOrEmptyString(results.front(), "error_message"));
    const std::string id(GetValueOrEmptyString(results.front(), "id"));

    return EntryUrl(id, url, domain, error_message);
}

unsigned FullTextCache::getSize() const {
    return full_text_cache_.size();
}

FullTextCache::TextType FullTextCache::MapTextDescriptionToTextType(const std::string &text_description) {
    const auto text_type(description_to_text_type_map.find(text_description));
    if (text_type == description_to_text_type_map.cend())
        return UNKNOWN;
    return text_type->second;
}

void FullTextCache::extractAndImportHTMLPages(const std::string &id, const std::string &full_text_location, const TextType &text_type) {
   const FileUtil::AutoTempDirectory auto_temp_dir("/tmp/ADT");
   const std::string html_export_directory(auto_temp_dir.getDirectoryPath());
   PdfUtil::ExtractHTMLAsPages(full_text_location, html_export_directory);
   FileUtil::Directory html_pages(html_export_directory, ".*-\\d+\\.html");
   for (const auto &html_page : html_pages) {
       static const auto page_number_matcher(RegexMatcher::RegexMatcherFactoryOrDie(".*-(\\d+)\\.html$"));
       const std::string page_file_name(html_export_directory + '/' + html_page.getName());
       if (not page_number_matcher->matched(page_file_name))
           LOG_ERROR("Invalid naming scheme for file \"" + page_file_name + "\"");
       const std::string page_number((*page_number_matcher)[1]);
       // Read in the file to full_text string
       std::ifstream page_file(page_file_name);
       std::stringstream full_text_stream;
       full_text_stream << page_file.rdbuf();
       std::string page_text(full_text_stream.str());
       full_text_cache_html_.simpleInsert({ { "id", id }, { "page", page_number },  { "full_text", page_text }, { "text_type", std::to_string(text_type) } });
   }
}

void FullTextCache::insertEntry(const std::string &id, const std::string &full_text,
                                const std::vector<EntryUrl> &entry_urls, const TextType &text_type)
{
    const time_t now(std::time(nullptr));
    Random::Rand rand(now);
    time_t expiration(TimeUtil::BAD_TIME_T);
    for (const auto &entry_url : entry_urls) {
        if (full_text.empty() and entry_url.error_message_.empty())
            LOG_ERROR("you must provide either data to be cached or a non-empty error message! (id " + id + ")");

        if (not entry_url.error_message_.empty())
            expiration = now + MIN_CACHE_EXPIRE_TIME_ON_ERROR + rand(MAX_CACHE_EXPIRE_TIME_ON_ERROR - MIN_CACHE_EXPIRE_TIME_ON_ERROR);
    }

    if (expiration == TimeUtil::BAD_TIME_T) {
        if (not full_text.empty())
            full_text_cache_.simpleInsert({ { "id", id }, { "full_text", full_text },  { "text_type", std::to_string(text_type) } });
    }
    else {
        const std::string expiration_string = TimeUtil::TimeTToString(expiration, TimeUtil::ISO_8601_FORMAT);
        if (full_text.empty())
            full_text_cache_.simpleInsert({ { "id", id }, { "expiration", expiration_string } });
        else
            full_text_cache_.simpleInsert({ { "id", id }, { "expiration", expiration_string }, { "full_text", full_text },
                                          { "text_type", std::to_string(text_type) } });
    }

    for (const auto &entry_url : entry_urls) {
        if (entry_url.error_message_.empty())
            full_text_cache_urls_.simpleInsert({ { "id", id }, { "url", entry_url.url_ }, { "domain", entry_url.domain_ } });
        else
            full_text_cache_urls_.simpleInsert({ { "id", id }, { "url", entry_url.url_ }, { "domain", entry_url.domain_ },
                                                 { "error_message", entry_url.error_message_ } });
    }

}

bool FullTextCache::deleteEntry(const std::string &id) {
    return full_text_cache_.deleteDocument(id) and full_text_cache_urls_.deleteDocument(id) and full_text_cache_html_.deleteDocument(id);
}
<commit_msg>Remove superfluous include<commit_after>/** \file   FullTextCache.cc
 *  \brief  Implementation of functions relating to our full-text cache.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2017-2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "FullTextCache.h"
#include <algorithm>
#include <ctime>
#include <sstream>
#include <tuple>
#include "Compiler.h"
#include "DbRow.h"
#include "FileUtil.h"
#include "PdfUtil.h"
#include "Random.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "TimeUtil.h"
#include "UrlUtil.h"
#include "util.h"
#include "VuFind.h"

constexpr unsigned MIN_CACHE_EXPIRE_TIME_ON_ERROR(42300 * 60); // About 1 month in seconds.
constexpr unsigned MAX_CACHE_EXPIRE_TIME_ON_ERROR(42300 * 60 * 2); // About 2 months in seconds.

static const std::map<std::string, FullTextCache::TextType> description_to_text_type_map {
    { "Volltext", FullTextCache::FULLTEXT },
    { "Inhaltsverzeichnis", FullTextCache::TOC },
    { "Inhaltstext", FullTextCache::ABSTRACT },
    { "Zusammenfassung", FullTextCache::SUMMARY }
};

bool FullTextCache::getDomainFromUrl(const std::string &url, std::string * const domain) const {
    std::string scheme, username_password, authority, port, path, params, query, fragment, relative_url;
    const bool result(UrlUtil::ParseUrl(url, &scheme, &username_password, &authority, &port, &path, &params, &query,
                                        &fragment, &relative_url));
    if (result)
        *domain = authority;

    return result;
}

bool FullTextCache::entryExpired(const std::string &id, std::vector<std::string> urls) {
    Entry entry;
    if (not getEntry(id, &entry))
        return true;
    const time_t now(std::time(nullptr));
    if (entry.expiration_ == TimeUtil::BAD_TIME_T or now < entry.expiration_) {
        std::vector<std::string> existing_urls(FullTextCache::getEntryUrlsAsStrings(id));

        std::sort(existing_urls.begin(), existing_urls.end());
        std::sort(urls.begin(), urls.end());
        if (urls == existing_urls)
            return false;
    }

    return true;
}

bool FullTextCache::singleUrlExpired(const std::string &id, const std::string &url) {
    Entry entry;
    if (not getEntry(id, &entry))
        return true;
    if (entry.expiration_ == TimeUtil::BAD_TIME_T or (std::time(nullptr) < entry.expiration_)) {
        for (const auto &entry_url : FullTextCache::getEntryUrls(id)) {
            if (entry_url.url_ == url and entry_url.error_message_.empty())
                return false;
        }
    }
    return true;
}

bool FullTextCache::dummyEntryExists(const std::string &id) {
    Entry entry;
    if (not getEntry(id, &entry))
        return false;
    for (const auto &entry_url : FullTextCache::getEntryUrls(id)) {
        if (entry_url.url_ == DUMMY_URL)
            return true;
    }
    return false;
}

void FullTextCache::expireEntries() {
    full_text_cache_urls_.deleteRange("expiration", Elasticsearch::RO_LTE, "now");
}

bool FullTextCache::getEntry(const std::string &id, Entry * const entry) const {
    const auto results(full_text_cache_.simpleSelect({ "expiration" }, "id", id));
    if (results.empty())
        return false;

    entry->id_ = id;
    if (results.front().find("expiration") == results.front().cend())
        entry->expiration_ = TimeUtil::BAD_TIME_T;
    else
        entry->expiration_ = TimeUtil::Iso8601StringToTimeT(results.front().find("expiration")->second);
    return true;
}

inline std::string GetValueOrEmptyString(const std::map<std::string, std::string> &map, const std::string &key) {
    const auto pair(map.find(key));
    return pair == map.cend() ? "" : pair->second;
}

std::vector<FullTextCache::EntryUrl> FullTextCache::getEntryUrls(const std::string &id) const {
    const auto results(full_text_cache_urls_.simpleSelect({ "url", "domain", "error_message" }, "id", id));

    std::vector<EntryUrl> entry_urls;
    entry_urls.reserve(results.size());
    for (const auto &map : results) {
        EntryUrl entry_url;
        entry_url.id_            = id;
        entry_url.url_           = GetValueOrEmptyString(map, "url");
        entry_url.domain_        = GetValueOrEmptyString(map, "domain");
        entry_url.error_message_ = GetValueOrEmptyString(map, "error_message");
        entry_urls.emplace_back(entry_url);
    }
    return entry_urls;
}

std::vector<std::string> FullTextCache::getEntryUrlsAsStrings(const std::string &id) const {
    const auto results(full_text_cache_urls_.simpleSelect({ "url" }, "id", id));

    std::vector<std::string> urls;
    for (const auto &map : results) {

        const auto url(GetValueOrEmptyString(map, "url"));
        if (not url.empty())
            urls.emplace_back(url);
    }
    return urls;
}

unsigned FullTextCache::getErrorCount() const {
    return full_text_cache_urls_.count({ { "error_message", "*" } });
}

bool FullTextCache::getFullText(const std::string &id, std::string * const full_text) const {
    const auto results(full_text_cache_.simpleSelect({ "full_text" }, "id", id));

    if (results.empty())
        return false;

    *full_text = GetValueOrEmptyString(results.front(), "full_text");
    return true;
}

const std::string US("\x1F"); // ASCII unit separator

std::vector<FullTextCache::EntryGroup> FullTextCache::getEntryGroupsByDomainAndErrorMessage() const {
    const auto results(full_text_cache_urls_.simpleSelect({ "url", "domain", "error_message", "id" }));

    std::unordered_map<std::string, std::tuple<std::string, std::string, unsigned>> domains_and_errors_to_ids_and_urls_and_counts_map;
    for (const auto &map : results) {
        const auto url_pair(map.find("url"));
        const auto domain_pair(map.find("domain"));
        const auto error_message_pair(map.find("error_message"));
        if (url_pair == map.cend() or domain_pair == map.cend() or error_message_pair == map.cend())
            continue;

        const auto id_pair(map.find("id"));
        const auto key(domain_pair->second + US + error_message_pair->second);
        auto id_and_url_and_count(domains_and_errors_to_ids_and_urls_and_counts_map.find(key));
        if (id_and_url_and_count != domains_and_errors_to_ids_and_urls_and_counts_map.end())
            ++std::get<2>(id_and_url_and_count->second);
        else
            domains_and_errors_to_ids_and_urls_and_counts_map[key] = std::make_tuple(id_pair->second, url_pair->second, 1);
    }

    std::vector<EntryGroup> groups;
    groups.reserve(domains_and_errors_to_ids_and_urls_and_counts_map.size());
    for (const auto &domain_and_error_to_id_and_url_and_count : domains_and_errors_to_ids_and_urls_and_counts_map) {
        std::vector<std::string> parts;
        if (unlikely(StringUtil::Split(domain_and_error_to_id_and_url_and_count.first, US, &parts) != 2))
            LOG_ERROR("This should never happen (" + std::to_string(parts.size()) + "): "
                      + StringUtil::CStyleEscape(domain_and_error_to_id_and_url_and_count.first));

        groups.emplace_back(EntryGroup(std::get<2>(domain_and_error_to_id_and_url_and_count.second),
                                       parts[0], parts[1],
                                       std::get<0>(domain_and_error_to_id_and_url_and_count.second),
                                       std::get<1>(domain_and_error_to_id_and_url_and_count.second)));
    }

    std::sort(groups.begin(), groups.end(), [](const EntryGroup &eg1, const EntryGroup &eg2){ return eg1.count_ > eg2.count_;});
    return groups;
}

std::vector<FullTextCache::EntryUrl> FullTextCache::getJoinedEntriesByDomainAndErrorMessage(const std::string &domain_,
                                                                                const std::string &error_message_) const
{
    const auto results(full_text_cache_urls_.simpleSelect({ "url", "domain", "error_message", "id" },
                                                          { { "domain", domain_ }, { "error_message", error_message_ } }));

    std::vector<EntryUrl> entries;
    for (const auto &map : results) {
        const std::string url(GetValueOrEmptyString(map, "url"));
        const std::string domain(GetValueOrEmptyString(map, "domain"));
        const std::string error_message(GetValueOrEmptyString(map, "error_message"));
        const std::string id(GetValueOrEmptyString(map, "id"));

        entries.emplace_back(EntryUrl(id, url, domain, error_message));
    }

    std::sort(entries.begin(), entries.end(), [](const EntryUrl &eu1, const EntryUrl &eu2){ return eu1.id_ < eu2.id_; });
    return entries;
}

FullTextCache::EntryUrl FullTextCache::getJoinedEntryByDomainAndErrorMessage(const std::string &domain_, const std::string &error_message_)
    const
{
    const auto results(full_text_cache_urls_.simpleSelect({ "url", "domain", "error_message", "id" },
                                                          { { "domain", domain_ }, { "error_message", error_message_ } },
                                                          /* max_count = */1));
    if (unlikely(results.size() != 1))
        LOG_ERROR("failed to get one entry!");

    const std::string url(GetValueOrEmptyString(results.front(), "url"));
    const std::string domain(GetValueOrEmptyString(results.front(), "domain"));
    const std::string error_message(GetValueOrEmptyString(results.front(), "error_message"));
    const std::string id(GetValueOrEmptyString(results.front(), "id"));

    return EntryUrl(id, url, domain, error_message);
}

unsigned FullTextCache::getSize() const {
    return full_text_cache_.size();
}

FullTextCache::TextType FullTextCache::MapTextDescriptionToTextType(const std::string &text_description) {
    const auto text_type(description_to_text_type_map.find(text_description));
    if (text_type == description_to_text_type_map.cend())
        return UNKNOWN;
    return text_type->second;
}

void FullTextCache::extractAndImportHTMLPages(const std::string &id, const std::string &full_text_location, const TextType &text_type) {
   const FileUtil::AutoTempDirectory auto_temp_dir("/tmp/ADT");
   const std::string html_export_directory(auto_temp_dir.getDirectoryPath());
   PdfUtil::ExtractHTMLAsPages(full_text_location, html_export_directory);
   FileUtil::Directory html_pages(html_export_directory, ".*-\\d+\\.html");
   for (const auto &html_page : html_pages) {
       static const auto page_number_matcher(RegexMatcher::RegexMatcherFactoryOrDie(".*-(\\d+)\\.html$"));
       const std::string page_file_name(html_export_directory + '/' + html_page.getName());
       if (not page_number_matcher->matched(page_file_name))
           LOG_ERROR("Invalid naming scheme for file \"" + page_file_name + "\"");
       const std::string page_number((*page_number_matcher)[1]);
       // Read in the file to full_text string
       std::ifstream page_file(page_file_name);
       std::stringstream full_text_stream;
       full_text_stream << page_file.rdbuf();
       std::string page_text(full_text_stream.str());
       full_text_cache_html_.simpleInsert({ { "id", id }, { "page", page_number },  { "full_text", page_text }, { "text_type", std::to_string(text_type) } });
   }
}

void FullTextCache::insertEntry(const std::string &id, const std::string &full_text,
                                const std::vector<EntryUrl> &entry_urls, const TextType &text_type)
{
    const time_t now(std::time(nullptr));
    Random::Rand rand(now);
    time_t expiration(TimeUtil::BAD_TIME_T);
    for (const auto &entry_url : entry_urls) {
        if (full_text.empty() and entry_url.error_message_.empty())
            LOG_ERROR("you must provide either data to be cached or a non-empty error message! (id " + id + ")");

        if (not entry_url.error_message_.empty())
            expiration = now + MIN_CACHE_EXPIRE_TIME_ON_ERROR + rand(MAX_CACHE_EXPIRE_TIME_ON_ERROR - MIN_CACHE_EXPIRE_TIME_ON_ERROR);
    }

    if (expiration == TimeUtil::BAD_TIME_T) {
        if (not full_text.empty())
            full_text_cache_.simpleInsert({ { "id", id }, { "full_text", full_text },  { "text_type", std::to_string(text_type) } });
    }
    else {
        const std::string expiration_string = TimeUtil::TimeTToString(expiration, TimeUtil::ISO_8601_FORMAT);
        if (full_text.empty())
            full_text_cache_.simpleInsert({ { "id", id }, { "expiration", expiration_string } });
        else
            full_text_cache_.simpleInsert({ { "id", id }, { "expiration", expiration_string }, { "full_text", full_text },
                                          { "text_type", std::to_string(text_type) } });
    }

    for (const auto &entry_url : entry_urls) {
        if (entry_url.error_message_.empty())
            full_text_cache_urls_.simpleInsert({ { "id", id }, { "url", entry_url.url_ }, { "domain", entry_url.domain_ } });
        else
            full_text_cache_urls_.simpleInsert({ { "id", id }, { "url", entry_url.url_ }, { "domain", entry_url.domain_ },
                                                 { "error_message", entry_url.error_message_ } });
    }

}

bool FullTextCache::deleteEntry(const std::string &id) {
    return full_text_cache_.deleteDocument(id) and full_text_cache_urls_.deleteDocument(id) and full_text_cache_html_.deleteDocument(id);
}
<|endoftext|>


<commit_before>#include "openlocationcode.h"
#include "codearea.h"

#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <string>

#include "gtest/gtest.h"

namespace openlocationcode {
namespace internal {
namespace {

TEST(ParameterChecks, PairCodeLengthIsEven) {
  EXPECT_EQ(0, internal::kPairCodeLength % 2);
}

TEST(ParameterChecks, AlphabetIsOrdered) {
  char last = 0;
  for (size_t i = 0; i < internal::kEncodingBase; i++) {
    EXPECT_TRUE(internal::kAlphabet[i] > last);
    last = internal::kAlphabet[i];
  }
}

TEST(ParameterChecks, SeparatorPositionValid) {
  EXPECT_TRUE(internal::kSeparatorPosition <= internal::kPairCodeLength);
}

TEST(ParameterChecks, ShortenDegreesValid) {
  EXPECT_TRUE(internal::kMinShortenDegrees >= internal::kGridSizeDegrees);
}

}  // namespace
}  // namespace internal

namespace {

std::vector<std::vector<std::string>> ParseCsv(std::string path_to_file) {
  std::vector<std::vector<std::string>> csv_records;
  std::string line;

  std::ifstream input_stream(path_to_file, std::ifstream::binary);
  while (std::getline(input_stream, line)) {
    // Ignore comments in the file
    if (line.at(0) == '#') {
      continue;
    }
    std::vector<std::string> line_records;
    std::stringstream lineStream(line);
    std::string cell;
    while (std::getline(lineStream, cell, ',')) {
      line_records.push_back(cell);
    }
    csv_records.push_back(line_records);
  }
  EXPECT_GT(csv_records.size(), 0);
  return csv_records;
}

struct EncodingTestData {
  std::string code;
  double lat_deg;
  double lng_deg;
  double lo_lat_deg;
  double lo_lng_deg;
  double hi_lat_deg;
  double hi_lng_deg;
};

class EncodingChecks : public ::testing::TestWithParam<EncodingTestData> {};

const std::string kEncodingTestsFile = "test_data/encodingTests.csv";

std::vector<EncodingTestData> GetEncodingDataFromCsv() {
  std::vector<EncodingTestData> data_results;
  std::vector<std::vector<std::string>> csv_records =
      ParseCsv(kEncodingTestsFile);
  for (size_t i = 0; i < csv_records.size(); i++) {
    EncodingTestData test_data = {};
    test_data.code = csv_records[i][0];
    test_data.lat_deg = strtod(csv_records[i][1].c_str(), nullptr);
    test_data.lng_deg = strtod(csv_records[i][2].c_str(), nullptr);
    test_data.lo_lat_deg = strtod(csv_records[i][3].c_str(), nullptr);
    test_data.lo_lng_deg = strtod(csv_records[i][4].c_str(), nullptr);
    test_data.hi_lat_deg = strtod(csv_records[i][5].c_str(), nullptr);
    test_data.hi_lng_deg = strtod(csv_records[i][6].c_str(), nullptr);
    data_results.push_back(test_data);
  }
  return data_results;
}

TEST_P(EncodingChecks, Encode) {
  EncodingTestData test_data = GetParam();
  CodeArea expected_rect =
      CodeArea(test_data.lo_lat_deg, test_data.lo_lng_deg, test_data.hi_lat_deg,
               test_data.hi_lng_deg, CodeLength(test_data.code));
  LatLng lat_lng = LatLng{test_data.lat_deg, test_data.lng_deg};
  // Encode the test location and make sure we get the expected code.
  std::string actual_code = Encode(lat_lng, CodeLength(test_data.code));
  EXPECT_EQ(test_data.code, actual_code);
  // Now decode the code and check we get the correct coordinates.
  CodeArea actual_rect = Decode(test_data.code);
  EXPECT_NEAR(expected_rect.GetCenter().latitude,
              actual_rect.GetCenter().latitude, 1e-10);
  EXPECT_NEAR(expected_rect.GetCenter().longitude,
              actual_rect.GetCenter().longitude, 1e-10);
}

INSTANTIATE_TEST_CASE_P(OLC_Tests, EncodingChecks,
                        ::testing::ValuesIn(GetEncodingDataFromCsv()));

struct ValidityTestData {
  std::string code;
  bool is_valid;
  bool is_short;
  bool is_full;
};

class ValidityChecks : public ::testing::TestWithParam<ValidityTestData> {};

const std::string kValidityTestsFile = "test_data/validityTests.csv";

std::vector<ValidityTestData> GetValidityDataFromCsv() {
  std::vector<ValidityTestData> data_results;
  std::vector<std::vector<std::string>> csv_records =
      ParseCsv(kValidityTestsFile);
  for (size_t i = 0; i < csv_records.size(); i++) {
    ValidityTestData test_data = {};
    test_data.code = csv_records[i][0];
    test_data.is_valid = csv_records[i][1] == "true";
    test_data.is_short = csv_records[i][2] == "true";
    test_data.is_full = csv_records[i][3] == "true";
    data_results.push_back(test_data);
  }
  return data_results;
}

TEST_P(ValidityChecks, Validity) {
  ValidityTestData test_data = GetParam();
  EXPECT_EQ(test_data.is_valid, IsValid(test_data.code));
  EXPECT_EQ(test_data.is_full, IsFull(test_data.code));
  EXPECT_EQ(test_data.is_short, IsShort(test_data.code));
}

INSTANTIATE_TEST_CASE_P(OLC_Tests, ValidityChecks,
                        ::testing::ValuesIn(GetValidityDataFromCsv()));

struct ShortCodeTestData {
  std::string full_code;
  double reference_lat;
  double reference_lng;
  std::string short_code;
  std::string test_type;
};

class ShortCodeChecks : public ::testing::TestWithParam<ShortCodeTestData> {};

const std::string kShortCodeTestsFile = "test_data/shortCodeTests.csv";

std::vector<ShortCodeTestData> GetShortCodeDataFromCsv() {
  std::vector<ShortCodeTestData> data_results;
  std::vector<std::vector<std::string>> csv_records =
      ParseCsv(kShortCodeTestsFile);
  for (size_t i = 0; i < csv_records.size(); i++) {
    ShortCodeTestData test_data = {};
    test_data.full_code = csv_records[i][0];
    test_data.reference_lat = strtod(csv_records[i][1].c_str(), nullptr);
    test_data.reference_lng = strtod(csv_records[i][2].c_str(), nullptr);
    test_data.short_code = csv_records[i][3];
    test_data.test_type = csv_records[i][4];
    data_results.push_back(test_data);
  }
  return data_results;
}

TEST_P(ShortCodeChecks, ShortCode) {
  ShortCodeTestData test_data = GetParam();
  LatLng reference_loc =
      LatLng{test_data.reference_lat, test_data.reference_lng};
  // Shorten the code using the reference location and check.
  if (test_data.test_type == "B" || test_data.test_type == "S") {
    std::string actual_short = Shorten(test_data.full_code, reference_loc);
    EXPECT_EQ(test_data.short_code, actual_short);
  }
  // Now extend the code using the reference location and check.
  if (test_data.test_type == "B" || test_data.test_type == "R") {
    std::string actual_full = RecoverNearest(test_data.short_code, reference_loc);
    EXPECT_EQ(test_data.full_code, actual_full);
  }
}

INSTANTIATE_TEST_CASE_P(OLC_Tests, ShortCodeChecks,
                        ::testing::ValuesIn(GetShortCodeDataFromCsv()));

TEST(MaxCodeLengthChecks, MaxCodeLength) {
  LatLng loc = LatLng{51.3701125, -10.202665625};
  // Check we do not return a code longer than is valid.
  std::string long_code = Encode(loc, 1000000);
  // The code length is the maximum digit count plus one for the separator.
  EXPECT_EQ(long_code.size(), 1 + internal::kMaximumDigitCount);
  EXPECT_TRUE(IsValid(long_code));
  Decode(long_code);
  // Extend the code and make sure it is no longer valid.
  std::string too_long_code = long_code + "W";
  EXPECT_FALSE(IsValid(too_long_code));
  // Ensure too many characters after the separator also causes a fail, even if
  // the total number of characters is ok.
  too_long_code = long_code.substr(6) + "WW";
  EXPECT_TRUE(too_long_code.size() < internal::kMaximumDigitCount);
  EXPECT_FALSE(IsValid(too_long_code));
}

}  // namespace
}  // namespace openlocationcode
<commit_msg>Update openlocationcode_test.cc<commit_after>#include "openlocationcode.h"

#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <string>

#include "codearea.h"
#include "gtest/gtest.h"

namespace openlocationcode {
namespace internal {
namespace {

TEST(ParameterChecks, PairCodeLengthIsEven) {
  EXPECT_EQ(0, internal::kPairCodeLength % 2);
}

TEST(ParameterChecks, AlphabetIsOrdered) {
  char last = 0;
  for (size_t i = 0; i < internal::kEncodingBase; i++) {
    EXPECT_TRUE(internal::kAlphabet[i] > last);
    last = internal::kAlphabet[i];
  }
}

TEST(ParameterChecks, SeparatorPositionValid) {
  EXPECT_TRUE(internal::kSeparatorPosition <= internal::kPairCodeLength);
}

TEST(ParameterChecks, ShortenDegreesValid) {
  EXPECT_TRUE(internal::kMinShortenDegrees >= internal::kGridSizeDegrees);
}

}  // namespace
}  // namespace internal

namespace {

std::vector<std::vector<std::string>> ParseCsv(std::string path_to_file) {
  std::vector<std::vector<std::string>> csv_records;
  std::string line;

  std::ifstream input_stream(path_to_file, std::ifstream::binary);
  while (std::getline(input_stream, line)) {
    // Ignore comments in the file
    if (line.at(0) == '#') {
      continue;
    }
    std::vector<std::string> line_records;
    std::stringstream lineStream(line);
    std::string cell;
    while (std::getline(lineStream, cell, ',')) {
      line_records.push_back(cell);
    }
    csv_records.push_back(line_records);
  }
  EXPECT_GT(csv_records.size(), 0);
  return csv_records;
}

struct EncodingTestData {
  std::string code;
  double lat_deg;
  double lng_deg;
  double lo_lat_deg;
  double lo_lng_deg;
  double hi_lat_deg;
  double hi_lng_deg;
};

class EncodingChecks : public ::testing::TestWithParam<EncodingTestData> {};

const std::string kEncodingTestsFile = "test_data/encodingTests.csv";

std::vector<EncodingTestData> GetEncodingDataFromCsv() {
  std::vector<EncodingTestData> data_results;
  std::vector<std::vector<std::string>> csv_records =
      ParseCsv(kEncodingTestsFile);
  for (size_t i = 0; i < csv_records.size(); i++) {
    EncodingTestData test_data = {};
    test_data.code = csv_records[i][0];
    test_data.lat_deg = strtod(csv_records[i][1].c_str(), nullptr);
    test_data.lng_deg = strtod(csv_records[i][2].c_str(), nullptr);
    test_data.lo_lat_deg = strtod(csv_records[i][3].c_str(), nullptr);
    test_data.lo_lng_deg = strtod(csv_records[i][4].c_str(), nullptr);
    test_data.hi_lat_deg = strtod(csv_records[i][5].c_str(), nullptr);
    test_data.hi_lng_deg = strtod(csv_records[i][6].c_str(), nullptr);
    data_results.push_back(test_data);
  }
  return data_results;
}

TEST_P(EncodingChecks, Encode) {
  EncodingTestData test_data = GetParam();
  CodeArea expected_rect =
      CodeArea(test_data.lo_lat_deg, test_data.lo_lng_deg, test_data.hi_lat_deg,
               test_data.hi_lng_deg, CodeLength(test_data.code));
  LatLng lat_lng = LatLng{test_data.lat_deg, test_data.lng_deg};
  // Encode the test location and make sure we get the expected code.
  std::string actual_code = Encode(lat_lng, CodeLength(test_data.code));
  EXPECT_EQ(test_data.code, actual_code);
  // Now decode the code and check we get the correct coordinates.
  CodeArea actual_rect = Decode(test_data.code);
  EXPECT_NEAR(expected_rect.GetCenter().latitude,
              actual_rect.GetCenter().latitude, 1e-10);
  EXPECT_NEAR(expected_rect.GetCenter().longitude,
              actual_rect.GetCenter().longitude, 1e-10);
}

INSTANTIATE_TEST_CASE_P(OLC_Tests, EncodingChecks,
                        ::testing::ValuesIn(GetEncodingDataFromCsv()));

struct ValidityTestData {
  std::string code;
  bool is_valid;
  bool is_short;
  bool is_full;
};

class ValidityChecks : public ::testing::TestWithParam<ValidityTestData> {};

const std::string kValidityTestsFile = "test_data/validityTests.csv";

std::vector<ValidityTestData> GetValidityDataFromCsv() {
  std::vector<ValidityTestData> data_results;
  std::vector<std::vector<std::string>> csv_records =
      ParseCsv(kValidityTestsFile);
  for (size_t i = 0; i < csv_records.size(); i++) {
    ValidityTestData test_data = {};
    test_data.code = csv_records[i][0];
    test_data.is_valid = csv_records[i][1] == "true";
    test_data.is_short = csv_records[i][2] == "true";
    test_data.is_full = csv_records[i][3] == "true";
    data_results.push_back(test_data);
  }
  return data_results;
}

TEST_P(ValidityChecks, Validity) {
  ValidityTestData test_data = GetParam();
  EXPECT_EQ(test_data.is_valid, IsValid(test_data.code));
  EXPECT_EQ(test_data.is_full, IsFull(test_data.code));
  EXPECT_EQ(test_data.is_short, IsShort(test_data.code));
}

INSTANTIATE_TEST_CASE_P(OLC_Tests, ValidityChecks,
                        ::testing::ValuesIn(GetValidityDataFromCsv()));

struct ShortCodeTestData {
  std::string full_code;
  double reference_lat;
  double reference_lng;
  std::string short_code;
  std::string test_type;
};

class ShortCodeChecks : public ::testing::TestWithParam<ShortCodeTestData> {};

const std::string kShortCodeTestsFile = "test_data/shortCodeTests.csv";

std::vector<ShortCodeTestData> GetShortCodeDataFromCsv() {
  std::vector<ShortCodeTestData> data_results;
  std::vector<std::vector<std::string>> csv_records =
      ParseCsv(kShortCodeTestsFile);
  for (size_t i = 0; i < csv_records.size(); i++) {
    ShortCodeTestData test_data = {};
    test_data.full_code = csv_records[i][0];
    test_data.reference_lat = strtod(csv_records[i][1].c_str(), nullptr);
    test_data.reference_lng = strtod(csv_records[i][2].c_str(), nullptr);
    test_data.short_code = csv_records[i][3];
    test_data.test_type = csv_records[i][4];
    data_results.push_back(test_data);
  }
  return data_results;
}

TEST_P(ShortCodeChecks, ShortCode) {
  ShortCodeTestData test_data = GetParam();
  LatLng reference_loc =
      LatLng{test_data.reference_lat, test_data.reference_lng};
  // Shorten the code using the reference location and check.
  if (test_data.test_type == "B" || test_data.test_type == "S") {
    std::string actual_short = Shorten(test_data.full_code, reference_loc);
    EXPECT_EQ(test_data.short_code, actual_short);
  }
  // Now extend the code using the reference location and check.
  if (test_data.test_type == "B" || test_data.test_type == "R") {
    std::string actual_full = RecoverNearest(test_data.short_code, reference_loc);
    EXPECT_EQ(test_data.full_code, actual_full);
  }
}

INSTANTIATE_TEST_CASE_P(OLC_Tests, ShortCodeChecks,
                        ::testing::ValuesIn(GetShortCodeDataFromCsv()));

TEST(MaxCodeLengthChecks, MaxCodeLength) {
  LatLng loc = LatLng{51.3701125, -10.202665625};
  // Check we do not return a code longer than is valid.
  std::string long_code = Encode(loc, 1000000);
  // The code length is the maximum digit count plus one for the separator.
  EXPECT_EQ(long_code.size(), 1 + internal::kMaximumDigitCount);
  EXPECT_TRUE(IsValid(long_code));
  Decode(long_code);
  // Extend the code and make sure it is no longer valid.
  std::string too_long_code = long_code + "W";
  EXPECT_FALSE(IsValid(too_long_code));
  // Ensure too many characters after the separator also causes a fail, even if
  // the total number of characters is ok.
  too_long_code = long_code.substr(6) + "WW";
  EXPECT_TRUE(too_long_code.size() < internal::kMaximumDigitCount);
  EXPECT_FALSE(IsValid(too_long_code));
}

}  // namespace
}  // namespace openlocationcode
<|endoftext|>