<commit_before>#include <gtest/gtest.h>

#include <map>
#include <sstream>
#include <string>

#include <boost/filesystem.hpp>
#include <sqlite3.h>

#include "prioritydb.h"

#define DEFAULT_MAX_SIZE 100000000LL

namespace fs = boost::filesystem;

class DBFixture : public ::testing::Test {
  protected:
    typedef std::map<std::string, std::string> Record;

    virtual void SetUp() {
        db_path_ = fs::temp_directory_path() / fs::path{"prism_test.db"};
        db_string_ = db_path_.native();
        table_name_ = "prism_data";
        fs::remove(db_path_);
    }

    virtual void TearDown() {
        fs::remove(db_path_);
    }

    sqlite3* open_db_() {
        sqlite3* sqlite_db;
        if (sqlite3_open(db_string_.data(), &sqlite_db) != SQLITE_OK) {
            throw PriorityDBException{sqlite3_errmsg(sqlite_db)};
        }
        return sqlite_db;
    }

    int close_db_(sqlite3* db) {
        return sqlite3_close(db);
    }

    static int callback_(void* response_ptr, int num_values, char** values, char** names) {
        auto response = (std::vector<Record>*) response_ptr;
        auto record = Record();
        for (int i = 0; i < num_values; ++i) {
            if (values[i]) {
                record[names[i]] = values[i];
            }
        }

        response->push_back(record);

        return 0;
    }

    std::vector<Record> execute_(const std::string& sql) {
        std::vector<Record> response;
        auto db = open_db_();
        char* error;
        int rc = sqlite3_exec(db, sql.data(), &callback_, &response, &error);
        if (rc != SQLITE_OK) {
            auto error_string = std::string{error};
            sqlite3_free(error);
            throw PriorityDBException{error_string};
        }

        return response;
    }

    fs::path db_path_;
    std::string db_string_;
    std::string table_name_;
};

TEST_F(DBFixture, EmptyDBTest) {
    EXPECT_FALSE(fs::exists(db_path_));
}

TEST_F(DBFixture, ConstructDBTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    EXPECT_TRUE(fs::exists(db_path_));
}

TEST_F(DBFixture, ConstructDBNoDestructTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    {
        PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
        EXPECT_TRUE(fs::exists(db_path_));
    }
    EXPECT_TRUE(fs::exists(db_path_));
}

TEST_F(DBFixture, ConstructDBMultipleTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    {
        PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
        ASSERT_TRUE(fs::exists(db_path_));
    }
    {
        PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
        EXPECT_TRUE(fs::exists(db_path_));
    }
    EXPECT_TRUE(fs::exists(db_path_));
}

TEST_F(DBFixture, ConstructThrowTest) {
    bool thrown = false;
    try {
        PriorityDB db{DEFAULT_MAX_SIZE, (fs::temp_directory_path() / fs::path{""}).native()};
    } catch (const PriorityDBException& e) {
        thrown = true;
        EXPECT_EQ(std::string{"unable to open database file"},
                  std::string{e.what()});
    }
    EXPECT_TRUE(thrown);
}

TEST_F(DBFixture, ConstructCurrentThrowTest) {
    bool thrown = false;
    try {
        PriorityDB db{DEFAULT_MAX_SIZE, (fs::temp_directory_path() / fs::path{"."}).native()};
    } catch (const PriorityDBException& e) {
        thrown = true;
        EXPECT_EQ(std::string{"unable to open database file"},
                  std::string{e.what()});
    }
    EXPECT_TRUE(thrown);
}

TEST_F(DBFixture, ConstructParentThrowTest) {
    bool thrown = false;
    try {
        PriorityDB db{DEFAULT_MAX_SIZE, (fs::temp_directory_path() / fs::path{".."}).native()};
    } catch (const PriorityDBException& e) {
        thrown = true;
        EXPECT_EQ(std::string{"unable to open database file"},
                  std::string{e.what()});
    }
    EXPECT_TRUE(thrown);
}

TEST_F(DBFixture, ConstructZeroSpaceTest) {
    bool thrown = false;
    try {
        PriorityDB db{0LL, db_string_};
    } catch (const PriorityDBException& e) {
        thrown = true;
        EXPECT_EQ(std::string{"Must specify a nonzero max_size"},
                  std::string{e.what()});
    }
    EXPECT_TRUE(thrown);
}

TEST_F(DBFixture, InitialDBTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    ASSERT_TRUE(fs::exists(db_path_));
    std::stringstream stream;
    stream << "SELECT name FROM sqlite_master WHERE type='table' AND name='"
           << table_name_
           << "';";
    auto response = execute_(stream.str());
    ASSERT_EQ(1, response.size());
    auto record = response[0];
    ASSERT_EQ(1, record.size());
    ASSERT_NE(record.end(), record.find("name"));
    EXPECT_EQ(std::string{"prism_data"}, record["name"]);
}

TEST_F(DBFixture, InitialEmptyDBTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    ASSERT_TRUE(fs::exists(db_path_));
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    EXPECT_EQ(0, response.size());
}

TEST_F(DBFixture, InitialDBAfterDestructorTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    {
        PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
        ASSERT_TRUE(fs::exists(db_path_));
    }
    ASSERT_TRUE(fs::exists(db_path_));
    std::stringstream stream;
    stream << "SELECT name FROM sqlite_master WHERE type='table' AND name='"
           << table_name_
           << "';";
    auto response = execute_(stream.str());
    ASSERT_EQ(1, response.size());
    auto record = response[0];
    ASSERT_EQ(1, record.size());
    ASSERT_NE(record.end(), record.find("name"));
    EXPECT_EQ(std::string{"prism_data"}, record["name"]);
}

TEST_F(DBFixture, InitialEmptyDBAfterDestructorTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    {
        PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
        ASSERT_TRUE(fs::exists(db_path_));
    }
    ASSERT_TRUE(fs::exists(db_path_));
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    EXPECT_EQ(0, response.size());
}

TEST_F(DBFixture, InsertEmptyHashTest) {
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    db.Insert(1, "", 5, false);
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    ASSERT_EQ(0, response.size());
}

TEST_F(DBFixture, InsertSingleTest) {
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    db.Insert(1, "hash", 5, false);
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    ASSERT_EQ(1, response.size());
    auto record = response[0];
    ASSERT_EQ(5, record.size());
    EXPECT_EQ(1, std::stoi(record["id"]));
    EXPECT_EQ(1, std::stoi(record["priority"]));
    EXPECT_EQ(std::string{"hash"}, record["hash"]);
    EXPECT_EQ(false, std::stoi(record["on_disk"]));
}

TEST_F(DBFixture, InsertCoupleTest) {
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    db.Insert(1, "hash", 5, false);
    db.Insert(3, "hashbrowns", 10, true);
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    for (auto& record : response) {
        for (auto item = record.begin(); item != record.end(); ++item) {
            std::cout << item->first << ": " << item->second << std::endl;
        }
    }
    ASSERT_EQ(2, response.size());
    {
        auto record = response[0];
        ASSERT_EQ(5, record.size());
        EXPECT_EQ(1, std::stoi(record["id"]));
        EXPECT_EQ(1, std::stoi(record["priority"]));
        EXPECT_EQ(std::string{"hash"}, record["hash"]);
        EXPECT_EQ(5, std::stoi(record["size"]));
        EXPECT_EQ(false, std::stoi(record["on_disk"]));
    }
    {
        auto record = response[1];
        ASSERT_EQ(5, record.size());
        EXPECT_EQ(2, std::stoi(record["id"]));
        EXPECT_EQ(3, std::stoi(record["priority"]));
        EXPECT_EQ(std::string{"hashbrowns"}, record["hash"]);
        EXPECT_EQ(10, std::stoi(record["size"]));
        EXPECT_EQ(true, std::stoi(record["on_disk"]));
    }
}

TEST_F(DBFixture, InsertManyTest) {
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    auto number_of_records = 100;
    for (int i = 0; i < number_of_records; ++i) {
        db.Insert(i, std::to_string(i * i), i * 2, i % 2);
    }
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    ASSERT_EQ(number_of_records, response.size());
    for (int i = 0; i < number_of_records; ++i) {
        auto record = response[i];
        ASSERT_EQ(5, record.size());
        EXPECT_EQ(i + 1, std::stoi(record["id"]));
        EXPECT_EQ(i, std::stoi(record["priority"]));
        EXPECT_EQ(std::to_string(i * i), record["hash"]);
        EXPECT_EQ(i * 2, std::stoi(record["size"]));
        EXPECT_EQ(i % 2, std::stoi(record["on_disk"]));
    }
}
<commit_msg>Remove bonus print<commit_after>#include <gtest/gtest.h>

#include <map>
#include <sstream>
#include <string>

#include <boost/filesystem.hpp>
#include <sqlite3.h>

#include "prioritydb.h"

#define DEFAULT_MAX_SIZE 100000000LL

namespace fs = boost::filesystem;

class DBFixture : public ::testing::Test {
  protected:
    typedef std::map<std::string, std::string> Record;

    virtual void SetUp() {
        db_path_ = fs::temp_directory_path() / fs::path{"prism_test.db"};
        db_string_ = db_path_.native();
        table_name_ = "prism_data";
        fs::remove(db_path_);
    }

    virtual void TearDown() {
        fs::remove(db_path_);
    }

    sqlite3* open_db_() {
        sqlite3* sqlite_db;
        if (sqlite3_open(db_string_.data(), &sqlite_db) != SQLITE_OK) {
            throw PriorityDBException{sqlite3_errmsg(sqlite_db)};
        }
        return sqlite_db;
    }

    int close_db_(sqlite3* db) {
        return sqlite3_close(db);
    }

    static int callback_(void* response_ptr, int num_values, char** values, char** names) {
        auto response = (std::vector<Record>*) response_ptr;
        auto record = Record();
        for (int i = 0; i < num_values; ++i) {
            if (values[i]) {
                record[names[i]] = values[i];
            }
        }

        response->push_back(record);

        return 0;
    }

    std::vector<Record> execute_(const std::string& sql) {
        std::vector<Record> response;
        auto db = open_db_();
        char* error;
        int rc = sqlite3_exec(db, sql.data(), &callback_, &response, &error);
        if (rc != SQLITE_OK) {
            auto error_string = std::string{error};
            sqlite3_free(error);
            throw PriorityDBException{error_string};
        }

        return response;
    }

    fs::path db_path_;
    std::string db_string_;
    std::string table_name_;
};

TEST_F(DBFixture, EmptyDBTest) {
    EXPECT_FALSE(fs::exists(db_path_));
}

TEST_F(DBFixture, ConstructDBTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    EXPECT_TRUE(fs::exists(db_path_));
}

TEST_F(DBFixture, ConstructDBNoDestructTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    {
        PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
        EXPECT_TRUE(fs::exists(db_path_));
    }
    EXPECT_TRUE(fs::exists(db_path_));
}

TEST_F(DBFixture, ConstructDBMultipleTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    {
        PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
        ASSERT_TRUE(fs::exists(db_path_));
    }
    {
        PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
        EXPECT_TRUE(fs::exists(db_path_));
    }
    EXPECT_TRUE(fs::exists(db_path_));
}

TEST_F(DBFixture, ConstructThrowTest) {
    bool thrown = false;
    try {
        PriorityDB db{DEFAULT_MAX_SIZE, (fs::temp_directory_path() / fs::path{""}).native()};
    } catch (const PriorityDBException& e) {
        thrown = true;
        EXPECT_EQ(std::string{"unable to open database file"},
                  std::string{e.what()});
    }
    EXPECT_TRUE(thrown);
}

TEST_F(DBFixture, ConstructCurrentThrowTest) {
    bool thrown = false;
    try {
        PriorityDB db{DEFAULT_MAX_SIZE, (fs::temp_directory_path() / fs::path{"."}).native()};
    } catch (const PriorityDBException& e) {
        thrown = true;
        EXPECT_EQ(std::string{"unable to open database file"},
                  std::string{e.what()});
    }
    EXPECT_TRUE(thrown);
}

TEST_F(DBFixture, ConstructParentThrowTest) {
    bool thrown = false;
    try {
        PriorityDB db{DEFAULT_MAX_SIZE, (fs::temp_directory_path() / fs::path{".."}).native()};
    } catch (const PriorityDBException& e) {
        thrown = true;
        EXPECT_EQ(std::string{"unable to open database file"},
                  std::string{e.what()});
    }
    EXPECT_TRUE(thrown);
}

TEST_F(DBFixture, ConstructZeroSpaceTest) {
    bool thrown = false;
    try {
        PriorityDB db{0LL, db_string_};
    } catch (const PriorityDBException& e) {
        thrown = true;
        EXPECT_EQ(std::string{"Must specify a nonzero max_size"},
                  std::string{e.what()});
    }
    EXPECT_TRUE(thrown);
}

TEST_F(DBFixture, InitialDBTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    ASSERT_TRUE(fs::exists(db_path_));
    std::stringstream stream;
    stream << "SELECT name FROM sqlite_master WHERE type='table' AND name='"
           << table_name_
           << "';";
    auto response = execute_(stream.str());
    ASSERT_EQ(1, response.size());
    auto record = response[0];
    ASSERT_EQ(1, record.size());
    ASSERT_NE(record.end(), record.find("name"));
    EXPECT_EQ(std::string{"prism_data"}, record["name"]);
}

TEST_F(DBFixture, InitialEmptyDBTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    ASSERT_TRUE(fs::exists(db_path_));
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    EXPECT_EQ(0, response.size());
}

TEST_F(DBFixture, InitialDBAfterDestructorTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    {
        PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
        ASSERT_TRUE(fs::exists(db_path_));
    }
    ASSERT_TRUE(fs::exists(db_path_));
    std::stringstream stream;
    stream << "SELECT name FROM sqlite_master WHERE type='table' AND name='"
           << table_name_
           << "';";
    auto response = execute_(stream.str());
    ASSERT_EQ(1, response.size());
    auto record = response[0];
    ASSERT_EQ(1, record.size());
    ASSERT_NE(record.end(), record.find("name"));
    EXPECT_EQ(std::string{"prism_data"}, record["name"]);
}

TEST_F(DBFixture, InitialEmptyDBAfterDestructorTest) {
    ASSERT_FALSE(fs::exists(db_path_));
    {
        PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
        ASSERT_TRUE(fs::exists(db_path_));
    }
    ASSERT_TRUE(fs::exists(db_path_));
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    EXPECT_EQ(0, response.size());
}

TEST_F(DBFixture, InsertEmptyHashTest) {
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    db.Insert(1, "", 5, false);
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    ASSERT_EQ(0, response.size());
}

TEST_F(DBFixture, InsertSingleTest) {
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    db.Insert(1, "hash", 5, false);
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    ASSERT_EQ(1, response.size());
    auto record = response[0];
    ASSERT_EQ(5, record.size());
    EXPECT_EQ(1, std::stoi(record["id"]));
    EXPECT_EQ(1, std::stoi(record["priority"]));
    EXPECT_EQ(std::string{"hash"}, record["hash"]);
    EXPECT_EQ(false, std::stoi(record["on_disk"]));
}

TEST_F(DBFixture, InsertCoupleTest) {
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    db.Insert(1, "hash", 5, false);
    db.Insert(3, "hashbrowns", 10, true);
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    ASSERT_EQ(2, response.size());
    {
        auto record = response[0];
        ASSERT_EQ(5, record.size());
        EXPECT_EQ(1, std::stoi(record["id"]));
        EXPECT_EQ(1, std::stoi(record["priority"]));
        EXPECT_EQ(std::string{"hash"}, record["hash"]);
        EXPECT_EQ(5, std::stoi(record["size"]));
        EXPECT_EQ(false, std::stoi(record["on_disk"]));
    }
    {
        auto record = response[1];
        ASSERT_EQ(5, record.size());
        EXPECT_EQ(2, std::stoi(record["id"]));
        EXPECT_EQ(3, std::stoi(record["priority"]));
        EXPECT_EQ(std::string{"hashbrowns"}, record["hash"]);
        EXPECT_EQ(10, std::stoi(record["size"]));
        EXPECT_EQ(true, std::stoi(record["on_disk"]));
    }
}

TEST_F(DBFixture, InsertManyTest) {
    PriorityDB db{DEFAULT_MAX_SIZE, db_string_};
    auto number_of_records = 100;
    for (int i = 0; i < number_of_records; ++i) {
        db.Insert(i, std::to_string(i * i), i * 2, i % 2);
    }
    std::stringstream stream;
    stream << "SELECT * FROM "
           << table_name_
           << ";";
    auto response = execute_(stream.str());
    ASSERT_EQ(number_of_records, response.size());
    for (int i = 0; i < number_of_records; ++i) {
        auto record = response[i];
        ASSERT_EQ(5, record.size());
        EXPECT_EQ(i + 1, std::stoi(record["id"]));
        EXPECT_EQ(i, std::stoi(record["priority"]));
        EXPECT_EQ(std::to_string(i * i), record["hash"]);
        EXPECT_EQ(i * 2, std::stoi(record["size"]));
        EXPECT_EQ(i % 2, std::stoi(record["on_disk"]));
    }
}
<|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_MAT_FUN_COV_DOT_PROD_HPP
#define STAN_MATH_PRIM_MAT_FUN_COV_DOT_PROD_HPP

#include <stan/math/prim/mat/fun/Eigen.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/err/check_nonnegative.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <stan/math/prim/scal/meta/return_type.hpp>
#include <stan/math/prim/mat/fun/dot_product.hpp>
#include <stan/math/prim/mat/fun/dot_self.hpp>
#include <boost/utility/enable_if.hpp>
#include <stan/math/prim/scal/meta/is_vector_like.hpp>
#include <stan/math/prim/scal/meta/is_constant.hpp>
#include <stan/math/prim/mat/meta/length.hpp>
#include <stan/math/prim/scal/fun/square.hpp>
#include <vector>

namespace stan {
namespace math {

/**
 * Returns a dot product covariance matrix. A member of Stan's Gaussian Process
 * Library.
 *
 * \f$k(x,x') = \sigma^2 + x \cdot x'\f$
 *
 * A dot product covariance matrix is the same covariance matrix
 * as in bayesian regression with \f$N(0,1)\f$ priors on regression coefficients
 * and a \f$N(0,\sigma^2)\f$ prior on the constant function. See Rasmussen and
 * Williams et al 2006, Chapter 4.
 *
 * @tparam T_x type of std::vector of elements
 * @tparam T_sigma type of sigma
 *
 * @param x std::vector of elements that can be used in dot product.
 *    This function assumes each element of x is the same size.
 * @param sigma constant function that can be used in stan::math::square
 * @return dot product covariance matrix that is positive semi-definite
 * @throw std::domain_error if sigma < 0, nan, inf or
 *   x is nan or infinite
 */
template <typename T_x, typename T_sigma>
inline typename boost::enable_if_c<
    is_vector_like<T_x>::value,
    Eigen::Matrix<typename return_type<T_x, T_sigma>::type, Eigen::Dynamic,
                  Eigen::Dynamic>>::type
gp_dot_prod_cov(const std::vector<T_x> &x, const T_sigma &sigma) {
  using stan::math::dot_product;
  using stan::math::square;

  check_not_nan("gp_dot_prod_cov", "sigma", sigma);
  check_nonnegative("gp_dot_prod_cov", "sigma", sigma);
  check_finite("gp_dot_prod_cov", "sigma", sigma);

  size_t x_size = x.size();
  size_t D = x[0].size();
  for (size_t i = 0; i < x_size; ++i) {
    for (size_t d = 0; d < D; ++d) {
      check_not_nan("gp_dot_prod_cov", "x", x[i][d]);
      check_finite("gp_dot_prod_cov", "x", x[i][d]);
    }
  }

  Eigen::Matrix<typename stan::return_type<T_x, T_sigma>::type, Eigen::Dynamic,
                Eigen::Dynamic>
      cov(x_size, x_size);
  if (x_size == 0)
    return cov;

  T_sigma sigma_sq = square(sigma);

  for (size_t i = 0; i < (x_size - 1); ++i) {
    cov(i, i) = sigma_sq + dot_self(x[i]);
    for (size_t j = i + 1; j < x_size; ++j) {
      cov(i, j) = sigma_sq + dot_product(x[i], x[j]);
      cov(j, i) = cov(i, j);
    }
  }
  cov(x_size - 1, x_size - 1) = sigma_sq + dot_self(x[x_size - 1]);
  return cov;
}

/**
 * Returns a dot product covariance matrix. A member of Stan's Gaussian
 * Process Library.
 *
 * \f$k(x,x') = \sigma^2 + x \cdot x'\f$
 *
 * A dot product covariance matrix is the same covariance matrix
 * as in bayesian regression with \f$N(0,1)\f$ priors on regression coefficients
 * and a \f$N(0,\sigma^2)\f$ prior on the constant function. See Rasmussen and
 * Williams et al 2006, Chapter 4.
 *
 * @tparam T_x type of std::vector of double
 * @tparam T_sigma type of sigma
 *
 * @param x std::vector of elements that can be used in transpose
 *   and multiply
 *    This function assumes each element of x is the same size.
 * @param sigma constant function that can be used in stan::math::square
 * @return dot product covariance matrix that is positive semi-definite
 * @throw std::domain_error if sigma < 0, nan, inf or
 *   x is nan or infinite
 */
template <typename T_x, typename T_sigma>
inline typename boost::enable_if_c<
    is_constant<T_x>::value,
    Eigen::Matrix<typename return_type<T_x, T_sigma>::type, Eigen::Dynamic,
                  Eigen::Dynamic>>::type
gp_dot_prod_cov(const std::vector<T_x> &x, const T_sigma &sigma) {
  using stan::math::dot_product;
  using stan::math::square;

  check_not_nan("gp_dot_prod_cov", "sigma", sigma);
  check_nonnegative("gp_dot_prod_cov", "sigma", sigma);
  check_finite("gp_dot_prod_cov", "sigma", sigma);

  size_t x_size = x.size();
  check_not_nan("gp_dot_prod_cov", "x", x);
  check_finite("gp_dot_prod_cov", "x", x);

  Eigen::Matrix<typename stan::return_type<T_x, T_sigma>::type, Eigen::Dynamic,
                Eigen::Dynamic>
      cov(x_size, x_size);
  if (x_size == 0)
    return cov;

  T_sigma sigma_sq = square(sigma);

  for (size_t i = 0; i < (x_size - 1); ++i) {
    cov(i, i) = sigma_sq + x[i] * x[i];
    for (size_t j = i + 1; j < x_size; ++j) {
      cov(i, j) = sigma_sq + x[i] * x[j];
      cov(j, i) = cov(i, j);
    }
  }
  cov(x_size - 1, x_size - 1) = sigma_sq + x[x_size - 1] * x[x_size - 1];
  return cov;
}

/**
 * Returns a dot product covariance matrix of differing
 * x's. A member of Stan's Gaussian Process Library.
 *
 * \f$k(x,x') = \sigma^2 + x \cdot x'\f$
 *
 * A dot product covariance matrix is the same covariance matrix
 * as in bayesian regression with \f$N(0,1)\f$ priors on regression coefficients
 * and a \f$N(0,\sigma^2)\f$ prior on the constant function. See Rasmussen and
 * Williams et al 2006, Chapter 4.
 *
 * @tparam T_x1 type of first std::vector of elements
 * @tparam T_x2 type of second std::vector of elements
 * @tparam T_sigma type of sigma
 *
 * @param x1 std::vector of elements that can be used in dot_product
 * @param x2 std::vector of elements that can be used in dot_product
 * @param sigma constant function that can be used in stan::math::square
 * @return dot product covariance matrix that is not always symmetric
 * @throw std::domain_error if sigma < 0, nan or inf
 *   or if x1 or x2 are nan or inf
 */
template <typename T_x1, typename T_x2, typename T_sigma>
inline typename boost::enable_if_c<
    is_vector_like<T_x1>::value,
    Eigen::Matrix<typename return_type<T_x1, T_x2, T_sigma>::type,
                  Eigen::Dynamic, Eigen::Dynamic>>::type
gp_dot_prod_cov(const std::vector<T_x1> &x1, const std::vector<T_x2> &x2,
                const T_sigma &sigma) {
  using stan::math::dot_product;
  using stan::math::square;

  check_not_nan("gp_dot_prod_cov", "sigma", sigma);
  check_nonnegative("gp_dot_prod_cov", "sigma", sigma);
  check_finite("gp_dot_prod_cov", "sigma", sigma);

  size_t x1_size = x1.size();
  size_t x2_size = x2.size();
  size_t D = x1[0].size();
  for (size_t i = 0; i < x1_size; ++i) {
    for (size_t d = 0; d < D; ++d) {
      check_not_nan("gp_dot_prod_cov", "x1", x1[i][d]);
      check_finite("gp_dot_prod_cov", "x1", x1[i][d]);
    }
  }
  for (size_t i = 0; i < x2_size; ++i) {
    for (size_t d = 0; d < D; ++d) {
      check_not_nan("gp_dot_prod_cov", "x2", x2[i][d]);
      check_finite("gp_dot_prod_cov", "x2", x2[i][d]);
    }
  }
  Eigen::Matrix<typename return_type<T_x1, T_x2, T_sigma>::type, Eigen::Dynamic,
                Eigen::Dynamic>
      cov(x1_size, x2_size);

  if (x1_size == 0 || x2_size == 0)
    return cov;

  T_sigma sigma_sq = square(sigma);

  for (size_t i = 0; i < x1_size; ++i) {
    for (size_t j = 0; j < x2_size; ++j) {
      cov(i, j) = sigma_sq + dot_product(x1[i], x2[j]);
    }
  }
  return cov;
}

/**
 * Returns a dot product covariance matrix of
 * differing x's. A member of Stan's Gaussian Process Library.
 *
 * \f$k(x,x') = \sigma^2 + x \cdot x'\f$
 *
 * A dot product covariance matrix is the same covariance matrix
 * as in bayesian regression with \f$N(0,1)\f$ priors on regression coefficients
 * and a \f$N(0,\sigma^2)\f$ prior on the constant function. See Rasmussen and
 * Williams et al 2006, Chapter 4.
 *
 * @tparam T_x1 type of first std::vector of double
 * @tparam T_x2 type of second std::vector of double
 * @tparam T_sigma type of sigma
 *
 * @param x1 std::vector of elements that can be used in dot_product
 * @param x2 std::vector of elements that can be used in dot_product
 * @param sigma is the constant function that can be used in stan::math::square
 * @return dot product covariance matrix that is not always symmetric
 * @throw std::domain_error if sigma < 0, nan or inf
 *   or if x1 or x2 are nan or inf
 */
template <typename T_x1, typename T_x2, typename T_sigma>
inline typename boost::enable_if_c<
    is_constant<T_x1>::value,
    Eigen::Matrix<typename return_type<T_x1, T_x2, T_sigma>::type,
                  Eigen::Dynamic, Eigen::Dynamic>>::type
gp_dot_prod_cov(const std::vector<T_x1> &x1, const std::vector<T_x2> &x2,
                const T_sigma &sigma) {
  using stan::math::square;

  check_not_nan("gp_dot_prod_cov", "sigma", sigma);
  check_nonnegative("gp_dot_prod_cov", "sigma", sigma);
  check_finite("gp_dot_prod_cov", "sigma", sigma);

  size_t x1_size = x1.size();
  size_t x2_size = x2.size();
  check_not_nan("gp_dot_prod_cov", "x1", x1);
  check_finite("gp_dot_prod_cov", "x1", x1);
  check_not_nan("gp_dot_prod_cov", "x2", x2);
  check_finite("gp_dot_prod_cov", "x2", x2);

  Eigen::Matrix<typename stan::return_type<T_x1, T_x2, T_sigma>::type,
                Eigen::Dynamic, Eigen::Dynamic>
      cov(x1_size, x2_size);

  if (x1_size == 0 || x2_size == 0)
    return cov;

  T_sigma sigma_sq = square(sigma);

  for (size_t i = 0; i < x1_size; ++i) {
    for (size_t j = 0; j < x2_size; ++j) {
      cov(i, j) = sigma_sq + x1[i] * x2[j];
    }
  }
  return cov;
}
}  // namespace math
}  // namespace stan
#endif
<commit_msg>vectorize out the D loop<commit_after>#ifndef STAN_MATH_PRIM_MAT_FUN_COV_DOT_PROD_HPP
#define STAN_MATH_PRIM_MAT_FUN_COV_DOT_PROD_HPP

#include <stan/math/prim/mat/fun/Eigen.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/err/check_nonnegative.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <stan/math/prim/scal/meta/return_type.hpp>
#include <stan/math/prim/mat/fun/dot_product.hpp>
#include <stan/math/prim/mat/fun/dot_self.hpp>
#include <boost/utility/enable_if.hpp>
#include <stan/math/prim/scal/meta/is_vector_like.hpp>
#include <stan/math/prim/scal/meta/is_constant.hpp>
#include <stan/math/prim/mat/meta/length.hpp>
#include <stan/math/prim/scal/fun/square.hpp>
#include <vector>

namespace stan {
namespace math {

/**
 * Returns a dot product covariance matrix. A member of Stan's Gaussian Process
 * Library.
 *
 * \f$k(x,x') = \sigma^2 + x \cdot x'\f$
 *
 * A dot product covariance matrix is the same covariance matrix
 * as in bayesian regression with \f$N(0,1)\f$ priors on regression coefficients
 * and a \f$N(0,\sigma^2)\f$ prior on the constant function. See Rasmussen and
 * Williams et al 2006, Chapter 4.
 *
 * @tparam T_x type of std::vector of elements
 * @tparam T_sigma type of sigma
 *
 * @param x std::vector of elements that can be used in dot product.
 *    This function assumes each element of x is the same size.
 * @param sigma constant function that can be used in stan::math::square
 * @return dot product covariance matrix that is positive semi-definite
 * @throw std::domain_error if sigma < 0, nan, inf or
 *   x is nan or infinite
 */
template <typename T_x, typename T_sigma>
inline typename boost::enable_if_c<
    is_vector_like<T_x>::value,
    Eigen::Matrix<typename return_type<T_x, T_sigma>::type, Eigen::Dynamic,
                  Eigen::Dynamic>>::type
gp_dot_prod_cov(const std::vector<T_x> &x, const T_sigma &sigma) {
  using stan::math::dot_product;
  using stan::math::square;

  check_not_nan("gp_dot_prod_cov", "sigma", sigma);
  check_nonnegative("gp_dot_prod_cov", "sigma", sigma);
  check_finite("gp_dot_prod_cov", "sigma", sigma);

  size_t x_size = x.size();
  for (size_t i = 0; i < x_size; ++i) {
      check_not_nan("gp_dot_prod_cov", "x", x[i]);
      check_finite("gp_dot_prod_cov", "x", x[i]);
  }

  Eigen::Matrix<typename stan::return_type<T_x, T_sigma>::type, Eigen::Dynamic,
                Eigen::Dynamic>
      cov(x_size, x_size);
  if (x_size == 0)
    return cov;

  T_sigma sigma_sq = square(sigma);

  for (size_t i = 0; i < (x_size - 1); ++i) {
    cov(i, i) = sigma_sq + dot_self(x[i]);
    for (size_t j = i + 1; j < x_size; ++j) {
      cov(i, j) = sigma_sq + dot_product(x[i], x[j]);
      cov(j, i) = cov(i, j);
    }
  }
  cov(x_size - 1, x_size - 1) = sigma_sq + dot_self(x[x_size - 1]);
  return cov;
}

/**
 * Returns a dot product covariance matrix. A member of Stan's Gaussian
 * Process Library.
 *
 * \f$k(x,x') = \sigma^2 + x \cdot x'\f$
 *
 * A dot product covariance matrix is the same covariance matrix
 * as in bayesian regression with \f$N(0,1)\f$ priors on regression coefficients
 * and a \f$N(0,\sigma^2)\f$ prior on the constant function. See Rasmussen and
 * Williams et al 2006, Chapter 4.
 *
 * @tparam T_x type of std::vector of double
 * @tparam T_sigma type of sigma
 *
 * @param x std::vector of elements that can be used in transpose
 *   and multiply
 *    This function assumes each element of x is the same size.
 * @param sigma constant function that can be used in stan::math::square
 * @return dot product covariance matrix that is positive semi-definite
 * @throw std::domain_error if sigma < 0, nan, inf or
 *   x is nan or infinite
 */
template <typename T_x, typename T_sigma>
inline typename boost::enable_if_c<
    is_constant<T_x>::value,
    Eigen::Matrix<typename return_type<T_x, T_sigma>::type, Eigen::Dynamic,
                  Eigen::Dynamic>>::type
gp_dot_prod_cov(const std::vector<T_x> &x, const T_sigma &sigma) {
  using stan::math::dot_product;
  using stan::math::square;

  check_not_nan("gp_dot_prod_cov", "sigma", sigma);
  check_nonnegative("gp_dot_prod_cov", "sigma", sigma);
  check_finite("gp_dot_prod_cov", "sigma", sigma);

  size_t x_size = x.size();
  check_not_nan("gp_dot_prod_cov", "x", x);
  check_finite("gp_dot_prod_cov", "x", x);

  Eigen::Matrix<typename stan::return_type<T_x, T_sigma>::type, Eigen::Dynamic,
                Eigen::Dynamic>
      cov(x_size, x_size);
  if (x_size == 0)
    return cov;

  T_sigma sigma_sq = square(sigma);

  for (size_t i = 0; i < (x_size - 1); ++i) {
    cov(i, i) = sigma_sq + x[i] * x[i];
    for (size_t j = i + 1; j < x_size; ++j) {
      cov(i, j) = sigma_sq + x[i] * x[j];
      cov(j, i) = cov(i, j);
    }
  }
  cov(x_size - 1, x_size - 1) = sigma_sq + x[x_size - 1] * x[x_size - 1];
  return cov;
}

/**
 * Returns a dot product covariance matrix of differing
 * x's. A member of Stan's Gaussian Process Library.
 *
 * \f$k(x,x') = \sigma^2 + x \cdot x'\f$
 *
 * A dot product covariance matrix is the same covariance matrix
 * as in bayesian regression with \f$N(0,1)\f$ priors on regression coefficients
 * and a \f$N(0,\sigma^2)\f$ prior on the constant function. See Rasmussen and
 * Williams et al 2006, Chapter 4.
 *
 * @tparam T_x1 type of first std::vector of elements
 * @tparam T_x2 type of second std::vector of elements
 * @tparam T_sigma type of sigma
 *
 * @param x1 std::vector of elements that can be used in dot_product
 * @param x2 std::vector of elements that can be used in dot_product
 * @param sigma constant function that can be used in stan::math::square
 * @return dot product covariance matrix that is not always symmetric
 * @throw std::domain_error if sigma < 0, nan or inf
 *   or if x1 or x2 are nan or inf
 */
template <typename T_x1, typename T_x2, typename T_sigma>
inline typename boost::enable_if_c<
    is_vector_like<T_x1>::value,
    Eigen::Matrix<typename return_type<T_x1, T_x2, T_sigma>::type,
                  Eigen::Dynamic, Eigen::Dynamic>>::type
gp_dot_prod_cov(const std::vector<T_x1> &x1, const std::vector<T_x2> &x2,
                const T_sigma &sigma) {
  using stan::math::dot_product;
  using stan::math::square;

  check_not_nan("gp_dot_prod_cov", "sigma", sigma);
  check_nonnegative("gp_dot_prod_cov", "sigma", sigma);
  check_finite("gp_dot_prod_cov", "sigma", sigma);

  size_t x1_size = x1.size();
  size_t x2_size = x2.size();
  for (size_t i = 0; i < x1_size; ++i) {
      check_not_nan("gp_dot_prod_cov", "x1", x1[i]);
      check_finite("gp_dot_prod_cov", "x1", x1[i]);
  }
  for (size_t i = 0; i < x2_size; ++i) {
      check_not_nan("gp_dot_prod_cov", "x2", x2[i]);
      check_finite("gp_dot_prod_cov", "x2", x2[i]);
  }
  Eigen::Matrix<typename return_type<T_x1, T_x2, T_sigma>::type, Eigen::Dynamic,
                Eigen::Dynamic>
      cov(x1_size, x2_size);

  if (x1_size == 0 || x2_size == 0)
    return cov;

  T_sigma sigma_sq = square(sigma);

  for (size_t i = 0; i < x1_size; ++i) {
    for (size_t j = 0; j < x2_size; ++j) {
      cov(i, j) = sigma_sq + dot_product(x1[i], x2[j]);
    }
  }
  return cov;
}

/**
 * Returns a dot product covariance matrix of
 * differing x's. A member of Stan's Gaussian Process Library.
 *
 * \f$k(x,x') = \sigma^2 + x \cdot x'\f$
 *
 * A dot product covariance matrix is the same covariance matrix
 * as in bayesian regression with \f$N(0,1)\f$ priors on regression coefficients
 * and a \f$N(0,\sigma^2)\f$ prior on the constant function. See Rasmussen and
 * Williams et al 2006, Chapter 4.
 *
 * @tparam T_x1 type of first std::vector of double
 * @tparam T_x2 type of second std::vector of double
 * @tparam T_sigma type of sigma
 *
 * @param x1 std::vector of elements that can be used in dot_product
 * @param x2 std::vector of elements that can be used in dot_product
 * @param sigma is the constant function that can be used in stan::math::square
 * @return dot product covariance matrix that is not always symmetric
 * @throw std::domain_error if sigma < 0, nan or inf
 *   or if x1 or x2 are nan or inf
 */
template <typename T_x1, typename T_x2, typename T_sigma>
inline typename boost::enable_if_c<
    is_constant<T_x1>::value,
    Eigen::Matrix<typename return_type<T_x1, T_x2, T_sigma>::type,
                  Eigen::Dynamic, Eigen::Dynamic>>::type
gp_dot_prod_cov(const std::vector<T_x1> &x1, const std::vector<T_x2> &x2,
                const T_sigma &sigma) {
  using stan::math::square;

  check_not_nan("gp_dot_prod_cov", "sigma", sigma);
  check_nonnegative("gp_dot_prod_cov", "sigma", sigma);
  check_finite("gp_dot_prod_cov", "sigma", sigma);

  size_t x1_size = x1.size();
  size_t x2_size = x2.size();
  check_not_nan("gp_dot_prod_cov", "x1", x1);
  check_finite("gp_dot_prod_cov", "x1", x1);
  check_not_nan("gp_dot_prod_cov", "x2", x2);
  check_finite("gp_dot_prod_cov", "x2", x2);

  Eigen::Matrix<typename stan::return_type<T_x1, T_x2, T_sigma>::type,
                Eigen::Dynamic, Eigen::Dynamic>
      cov(x1_size, x2_size);

  if (x1_size == 0 || x2_size == 0)
    return cov;

  T_sigma sigma_sq = square(sigma);

  for (size_t i = 0; i < x1_size; ++i) {
    for (size_t j = 0; j < x2_size; ++j) {
      cov(i, j) = sigma_sq + x1[i] * x2[j];
    }
  }
  return cov;
}
}  // namespace math
}  // namespace stan
#endif
<|endoftext|>


<commit_before>//
// Copyright (C) 2017~2017 by CSSlayer
// wengxt@gmail.com
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; see the file COPYING. If not,
// see <http://www.gnu.org/licenses/>.
//

#include "cloudpinyin.h"
#include <fcitx-config/iniparser.h>
#include <fcitx-utils/fs.h>
#include <fcitx-utils/standardpath.h>
#include <fcitx-utils/unixfd.h>
#include <fcitx-utils/utf8.h>
#include <fcitx/addonmanager.h>
#include <fcntl.h>
#include <thread>
#include <unistd.h>

using namespace fcitx;

class GoogleBackend : public Backend {
public:
    void prepareRequest(CurlQueue *queue, const std::string &pinyin) override {
        std::string url =
            "https://www.google.com/inputtools/request?ime=pinyin&text=";
        std::unique_ptr<char, decltype(&curl_free)> escaped(
            curl_escape(pinyin.c_str(), pinyin.size()), &curl_free);
        url += escaped.get();
        curl_easy_setopt(queue->curl(), CURLOPT_URL, url.c_str());
    }
    std::string parseResult(CurlQueue *queue) override {
        std::string result(queue->result().begin(), queue->result().end());
        auto start = result.find("\",[\"");
        std::string hanzi;
        if (start != std::string::npos) {
            start += strlen("\",[\"");
            auto end = result.find("\"", start);
            if (end != std::string::npos && end > start) {
                hanzi = result.substr(start, end - start);
            }
        }
        return hanzi;
    }
};

class BaiduBackend : public Backend {
public:
    void prepareRequest(CurlQueue *queue, const std::string &pinyin) override {
        std::string url = "https://olime.baidu.com/py?rn=0&pn=1&ol=1&py=";
        std::unique_ptr<char, decltype(&curl_free)> escaped(
            curl_escape(pinyin.c_str(), pinyin.size()), &curl_free);
        url += escaped.get();
        curl_easy_setopt(queue->curl(), CURLOPT_URL, url.c_str());
    }

    static inline bool ishex(char ch) {
        if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
            (ch >= 'A' && ch <= 'F'))
            return true;
        return false;
    }

    static inline unsigned char tohex(char ch) {
        if (ch >= '0' && ch <= '9')
            return ch - '0';
        if (ch >= 'a' && ch <= 'f')
            return ch - 'a' + 10;
        if (ch >= 'A' && ch <= 'F')
            return ch - 'A' + 10;
        return 0;
    }

    std::string parseResult(CurlQueue *queue) override {
        std::string result(queue->result().begin(), queue->result().end());
        auto start = result.find("[[[\"");
        std::string hanzi;
        if (start != std::string::npos) {
            start += strlen("[[[\"");
            auto end = result.find("\",", start);
            if (end != std::string::npos && end > start) {
                size_t length = end - start;
                if (length % 6 == 0) {
                }

                size_t i = start;
                while (i < end) {
                    if (result[i] == '\\' && result[i + 1] == 'u') {
                        if (ishex(result[i + 2]) && ishex(result[i + 3]) &&
                            ishex(result[i + 4]) && ishex(result[i + 5])) {
                            auto hi = (tohex(result[i + 2]) << 4) |
                                      tohex(result[i + 3]);
                            auto lo = (tohex(result[i + 4]) << 4) |
                                      tohex(result[i + 5]);
                            auto c = hi << 8 | lo;
                            hanzi += utf8::UCS4ToUTF8(c);
                        } else
                            break;
                    }

                    i += 6;
                }

                if (i != end) {
                    hanzi.clear();
                }
            }
        }
        return hanzi;
    }
};

constexpr int MAX_ERROR = 10;
constexpr int minInUs = 60000000;

CloudPinyin::CloudPinyin(fcitx::AddonManager *manager)
    : eventLoop_(manager->eventLoop()) {
    curl_global_init(CURL_GLOBAL_ALL);
    UnixFD pipe1Fd[2];

    int pipe1[2];
    if (pipe2(pipe1, O_NONBLOCK) < 0) {
        throw std::runtime_error("Failed to create pipe");
    }
    pipe1Fd[0].give(pipe1[0]);
    pipe1Fd[1].give(pipe1[1]);

    recvFd_.give(pipe1Fd[0].release());

    backends_.emplace(CloudPinyinBackend::Google,
                      std::make_unique<GoogleBackend>());
    backends_.emplace(CloudPinyinBackend::Baidu,
                      std::make_unique<BaiduBackend>());

    event_ = eventLoop_->addIOEvent(
        recvFd_.fd(), IOEventFlag::In,
        [this](EventSourceIO *, int, IOEventFlags) {
            char c;
            while (fs::safeRead(recvFd_.fd(), &c, sizeof(char)) > 0)
                ;
            CurlQueue *item;
            auto backend = config_.backend.value();
            auto iter = backends_.find(backend);
            Backend *b = nullptr;
            if (iter != backends_.end()) {
                b = iter->second.get();
            }

            while ((item = thread_->popFinished())) {
                if (item->httpCode() != 200) {
                    errorCount_ += 1;

                    if (errorCount_ == MAX_ERROR && resetError_) {
                        FCITX_ERROR() << "Cloud pinyin reaches max error. "
                                         "Retry in 5 minutes.";
                        resetError_->setNextInterval(minInUs * 5);
                        resetError_->setOneShot();
                    }
                }

                std::string hanzi;
                if (b) {
                    hanzi = b->parseResult(item);
                } else {
                    hanzi = "";
                }
                item->callback()(item->pinyin(), hanzi);
                if (hanzi.size()) {
                    cache_.insert(item->pinyin(), hanzi);
                }
                item->release();
            }
            return true;
        });

    resetError_ =
        eventLoop_->addTimeEvent(CLOCK_MONOTONIC, now(CLOCK_MONOTONIC), minInUs,
                                 [this](EventSourceTime *, uint64_t) {
                                     resetError();
                                     return true;
                                 });
    if (resetError_) {
        resetError_->setEnabled(false);
    }
    thread_ = std::make_unique<FetchThread>(std::move(pipe1Fd[1]));
}

CloudPinyin::~CloudPinyin() {}

void CloudPinyin::reloadConfig() {
    readAsIni(config_, "conf/cloudpinyin.conf");
}

void CloudPinyin::request(const std::string &pinyin,
                          CloudPinyinCallback callback) {
    if (static_cast<int>(pinyin.size()) < config_.minimumLength.value()) {
        callback(pinyin, "");
        return;
    }
    if (auto value = cache_.find(pinyin)) {
        callback(pinyin, *value);
    } else {
        auto backend = config_.backend.value();
        auto iter = backends_.find(backend);
        if (iter == backends_.end() || errorCount_ >= MAX_ERROR) {
            callback(pinyin, "");
            return;
        }
        auto b = iter->second.get();
        if (!thread_->addRequest([b, &pinyin, &callback](CurlQueue *queue) {
                b->prepareRequest(queue, pinyin);
                queue->setPinyin(pinyin);
                queue->setBusy();
                queue->setCallback(callback);
            })) {
            callback(pinyin, "");
        };
    }
}

FCITX_ADDON_FACTORY(CloudPinyinFactory);
<commit_msg>Fix Baidu backend for cloudpinyin<commit_after>//
// Copyright (C) 2017~2017 by CSSlayer
// wengxt@gmail.com
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; see the file COPYING. If not,
// see <http://www.gnu.org/licenses/>.
//

#include "cloudpinyin.h"
#include <fcitx-config/iniparser.h>
#include <fcitx-utils/fs.h>
#include <fcitx-utils/standardpath.h>
#include <fcitx-utils/unixfd.h>
#include <fcitx-utils/utf8.h>
#include <fcitx/addonmanager.h>
#include <fcntl.h>
#include <thread>
#include <unistd.h>

using namespace fcitx;

class GoogleBackend : public Backend {
public:
    void prepareRequest(CurlQueue *queue, const std::string &pinyin) override {
        std::string url =
            "https://www.google.com/inputtools/request?ime=pinyin&text=";
        std::unique_ptr<char, decltype(&curl_free)> escaped(
            curl_escape(pinyin.c_str(), pinyin.size()), &curl_free);
        url += escaped.get();
        curl_easy_setopt(queue->curl(), CURLOPT_URL, url.c_str());
    }
    std::string parseResult(CurlQueue *queue) override {
        std::string result(queue->result().begin(), queue->result().end());
        auto start = result.find("\",[\"");
        std::string hanzi;
        if (start != std::string::npos) {
            start += strlen("\",[\"");
            auto end = result.find("\"", start);
            if (end != std::string::npos && end > start) {
                hanzi = result.substr(start, end - start);
            }
        }
        return hanzi;
    }
};

class BaiduBackend : public Backend {
public:
    void prepareRequest(CurlQueue *queue, const std::string &pinyin) override {
        std::string url = "https://olime.baidu.com/py?rn=0&pn=1&ol=1&py=";
        std::unique_ptr<char, decltype(&curl_free)> escaped(
            curl_escape(pinyin.c_str(), pinyin.size()), &curl_free);
        url += escaped.get();
        curl_easy_setopt(queue->curl(), CURLOPT_URL, url.c_str());
    }

    std::string parseResult(CurlQueue *queue) override {
        std::string result(queue->result().begin(), queue->result().end());
        auto start = result.find("[[\"");
        std::string hanzi;
        if (start != std::string::npos) {
            start += strlen("[[\"");
            auto end = result.find("\",", start);
            if (end != std::string::npos && end > start) {
                hanzi = result.substr(start, end - start);
            }
        }
        return hanzi;
    }
};

constexpr int MAX_ERROR = 10;
constexpr int minInUs = 60000000;

CloudPinyin::CloudPinyin(fcitx::AddonManager *manager)
    : eventLoop_(manager->eventLoop()) {
    curl_global_init(CURL_GLOBAL_ALL);
    UnixFD pipe1Fd[2];

    int pipe1[2];
    if (pipe2(pipe1, O_NONBLOCK) < 0) {
        throw std::runtime_error("Failed to create pipe");
    }
    pipe1Fd[0].give(pipe1[0]);
    pipe1Fd[1].give(pipe1[1]);

    recvFd_.give(pipe1Fd[0].release());

    backends_.emplace(CloudPinyinBackend::Google,
                      std::make_unique<GoogleBackend>());
    backends_.emplace(CloudPinyinBackend::Baidu,
                      std::make_unique<BaiduBackend>());

    event_ = eventLoop_->addIOEvent(
        recvFd_.fd(), IOEventFlag::In,
        [this](EventSourceIO *, int, IOEventFlags) {
            char c;
            while (fs::safeRead(recvFd_.fd(), &c, sizeof(char)) > 0)
                ;
            CurlQueue *item;
            auto backend = config_.backend.value();
            auto iter = backends_.find(backend);
            Backend *b = nullptr;
            if (iter != backends_.end()) {
                b = iter->second.get();
            }

            while ((item = thread_->popFinished())) {
                if (item->httpCode() != 200) {
                    errorCount_ += 1;

                    if (errorCount_ == MAX_ERROR && resetError_) {
                        FCITX_ERROR() << "Cloud pinyin reaches max error. "
                                         "Retry in 5 minutes.";
                        resetError_->setNextInterval(minInUs * 5);
                        resetError_->setOneShot();
                    }
                }

                std::string hanzi;
                if (b) {
                    hanzi = b->parseResult(item);
                } else {
                    hanzi = "";
                }
                item->callback()(item->pinyin(), hanzi);
                if (hanzi.size()) {
                    cache_.insert(item->pinyin(), hanzi);
                }
                item->release();
            }
            return true;
        });

    resetError_ =
        eventLoop_->addTimeEvent(CLOCK_MONOTONIC, now(CLOCK_MONOTONIC), minInUs,
                                 [this](EventSourceTime *, uint64_t) {
                                     resetError();
                                     return true;
                                 });
    if (resetError_) {
        resetError_->setEnabled(false);
    }
    thread_ = std::make_unique<FetchThread>(std::move(pipe1Fd[1]));
}

CloudPinyin::~CloudPinyin() {}

void CloudPinyin::reloadConfig() {
    readAsIni(config_, "conf/cloudpinyin.conf");
}

void CloudPinyin::request(const std::string &pinyin,
                          CloudPinyinCallback callback) {
    if (static_cast<int>(pinyin.size()) < config_.minimumLength.value()) {
        callback(pinyin, "");
        return;
    }
    if (auto value = cache_.find(pinyin)) {
        callback(pinyin, *value);
    } else {
        auto backend = config_.backend.value();
        auto iter = backends_.find(backend);
        if (iter == backends_.end() || errorCount_ >= MAX_ERROR) {
            callback(pinyin, "");
            return;
        }
        auto b = iter->second.get();
        if (!thread_->addRequest([b, &pinyin, &callback](CurlQueue *queue) {
                b->prepareRequest(queue, pinyin);
                queue->setPinyin(pinyin);
                queue->setBusy();
                queue->setCallback(callback);
            })) {
            callback(pinyin, "");
        };
    }
}

FCITX_ADDON_FACTORY(CloudPinyinFactory);
<|endoftext|>


<commit_before>/*
 * Tags.cpp
 *****************************************************************************
 * Copyright © 2015 - VideoLAN and VLC Authors
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
#include "Tags.hpp"
#include <sstream>
#include <stack>

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>

using namespace hls::playlist;

Attribute::Attribute(const std::string &name_, const std::string &value_)
{
    name = name_;
    value = value_;
}

uint64_t Attribute::decimal() const
{
    std::istringstream is(value);
    uint64_t ret;
    is >> ret;
    return ret;
}

double Attribute::floatingPoint() const
{
    std::istringstream is(value);
    is.imbue(std::locale(""));
    double ret;
    is >> ret;
    return ret;
}

std::vector<uint8_t> Attribute::hexSequence() const
{
    std::vector<uint8_t> ret;
    if(value.length() > 2 && (value.substr(0, 2) == "0X" || value.substr(0, 2) == "0x") )
    {
        for(size_t i=2; i<=(value.length() - 2); i+=2)
        {
            unsigned val;
            std::stringstream ss(value.substr(i, 2));
            ss >> std::hex >> val;
            ret.push_back(val);
        }
    }
    return ret;
}

std::pair<std::size_t,std::size_t> Attribute::getByteRange() const
{
    std::size_t length = 0;
    std::size_t offset = 0;
    std::istringstream is(value);

    if(!is.eof())
    {
        is >> length;
        if(!is.eof())
        {
            char c = is.get();
            if(c == '@' && !is.eof())
                is >> offset;
        }
    }

    return std::make_pair(offset, length);
}

std::pair<int, int> Attribute::getResolution() const
{
    int w = 0, h = 0;

    std::istringstream is(value);
    if(!is.eof())
    {
        is >> w;
        if(!is.eof())
        {
            char c = is.get();
            if(c == 'x' && !is.eof())
                is >> h;
        }
    }

    return std::make_pair(w, h);
}

std::string Attribute::quotedString() const
{
    if(value.length() < 2)
        return "";

    std::istringstream is(value.substr(1, value.length() - 2));
    std::ostringstream os;

    char c;
    while(is.get(c))
    {
        if(c == '\\')
        {
            if(!is.get(c))
                break;
        }

        os << c;
    }

    return os.str();
}

Tag::Tag(int type_)
{
    type = type_;
}

Tag::~Tag()
{
}

int Tag::getType() const
{
    return type;
}

SingleValueTag::SingleValueTag(int type, const std::string &v)
    : Tag(type), attr("", v)
{
}

SingleValueTag::~SingleValueTag()
{

}

const Attribute &SingleValueTag::getValue() const
{
    return attr;
}

AttributesTag::AttributesTag(int type, const std::string &v) : Tag(type)
{
    parseAttributes(v);
}

AttributesTag::~AttributesTag()
{
    std::list<Attribute *>::const_iterator it;
    for(it = attributes.begin(); it != attributes.end(); ++it)
        delete *it;
}

const Attribute * AttributesTag::getAttributeByName(const char *name) const
{
    std::list<Attribute *>::const_iterator it;
    for(it = attributes.begin(); it != attributes.end(); ++it)
        if((*it)->name == name)
            return *it;

    return NULL;
}

void AttributesTag::addAttribute(Attribute *attr)
{
    attributes.push_back(attr);
}

void AttributesTag::parseAttributes(const std::string &field)
{
    std::istringstream iss(field);
    std::ostringstream oss;

    while(!iss.eof())
    {
        /* parse attribute name */
        while(!iss.eof())
        {
            char c = iss.peek();
            if((c >= 'A' && c <= 'Z') || c == '-')
            {
                oss.put((char)iss.get());
            }
            else if(c == '=')
            {
                iss.get();
                break;
            }
            else /* out of range */
                return;
        }

        std::string attrname = oss.str();
        oss.str("");

        /* parse attributes value */
        bool b_quoted = false;
        while(!iss.eof())
        {
            char c = iss.peek();
            if(c == '\\' && b_quoted)
            {
                iss.get();
            }
            else if(c == ',' && !b_quoted)
            {
                iss.get();
                break;
            }
            else if(c == '"')
            {
                b_quoted = !b_quoted;
            }

            if(!iss.eof())
                oss.put((char)iss.get());
        }

        std::string attrvalue = oss.str();
        oss.str("");

        Attribute *attribute = new (std::nothrow) Attribute(attrname, attrvalue);
        if(attribute)
            attributes.push_back(attribute);
    }
}

ValuesListTag::ValuesListTag(int type, const std::string &v) : AttributesTag(type, v)
{
    parseAttributes(v);
}

ValuesListTag::~ValuesListTag()
{
}

void ValuesListTag::parseAttributes(const std::string &field)
{
    std::size_t pos = field.find(',');
    if(pos != std::string::npos)
    {
        Attribute *attr = new (std::nothrow) Attribute("DURATION", field.substr(0, pos));
        if(attr)
            addAttribute(attr);

        attr = new (std::nothrow) Attribute("TITLE", field.substr(pos));
        if(attr)
            addAttribute(attr);
    }
}

Tag * TagFactory::createTagByName(const std::string &name, const std::string &value)
{
    struct
    {
        const char *psz;
        const int i;
    } const exttagmapping[] = {
        {"EXT-X-BYTERANGE",                 SingleValueTag::EXTXBYTERANGE},
        {"EXT-X-DISCONTINUITY",             Tag::EXTXDISCONTINUITY},
        {"EXT-X-KEY",                       AttributesTag::EXTXKEY},
        {"EXT-X-MAP",                       AttributesTag::EXTXMAP},
        {"EXT-X-PROGRAM-DATE-TIME",         SingleValueTag::EXTXPROGRAMDATETIME},
        {"EXT-X-TARGETDURATION",            SingleValueTag::EXTXTARGETDURATION},
        {"EXT-X-MEDIA-SEQUENCE",            SingleValueTag::EXTXMEDIASEQUENCE},
        {"EXT-X-DISCONTINUITY-SEQUENCE",    SingleValueTag::EXTXDISCONTINUITYSEQUENCE},
        {"EXT-X-ENDLIST",                   Tag::EXTXENDLIST},
        {"EXT-X-PLAYLIST-TYPE",             SingleValueTag::EXTXPLAYLISTTYPE},
        {"EXT-X-I-FRAMES-ONLY",             Tag::EXTXIFRAMESONLY},
        {"EXT-X-MEDIA",                     AttributesTag::EXTXMEDIA},
        {"EXT-X-STREAM-INF",                AttributesTag::EXTXSTREAMINF},
        {"EXTINF",                          ValuesListTag::EXTINF},
        {"",                                SingleValueTag::URI},
        {NULL,                              0},
    };

    for(int i=0; exttagmapping[i].psz; i++)
    {
        if(name != exttagmapping[i].psz)
            continue;

        switch(exttagmapping[i].i)
        {
        case Tag::EXTXDISCONTINUITY:
        case Tag::EXTXENDLIST:
        case Tag::EXTXIFRAMESONLY:
            return new (std::nothrow) Tag(exttagmapping[i].i);

        case SingleValueTag::URI:
        case SingleValueTag::EXTXVERSION:
        case SingleValueTag::EXTXBYTERANGE:
        case SingleValueTag::EXTXPROGRAMDATETIME:
        case SingleValueTag::EXTXTARGETDURATION:
        case SingleValueTag::EXTXMEDIASEQUENCE:
        case SingleValueTag::EXTXDISCONTINUITYSEQUENCE:
        case SingleValueTag::EXTXPLAYLISTTYPE:
            return new (std::nothrow) SingleValueTag(exttagmapping[i].i, value);

        case ValuesListTag::EXTINF:
            return new (std::nothrow) ValuesListTag(exttagmapping[i].i, value);

        case AttributesTag::EXTXKEY:
        case AttributesTag::EXTXMAP:
        case AttributesTag::EXTXMEDIA:
        case AttributesTag::EXTXSTREAMINF:
            return new (std::nothrow) AttributesTag(exttagmapping[i].i, value);
        }

    }

    return NULL;
}
<commit_msg>HLS: always use "C" locale to parse float<commit_after>/*
 * Tags.cpp
 *****************************************************************************
 * Copyright © 2015 - VideoLAN and VLC Authors
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
#include "Tags.hpp"
#include <sstream>
#include <stack>

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>

using namespace hls::playlist;

Attribute::Attribute(const std::string &name_, const std::string &value_)
{
    name = name_;
    value = value_;
}

uint64_t Attribute::decimal() const
{
    std::istringstream is(value);
    uint64_t ret;
    is >> ret;
    return ret;
}

double Attribute::floatingPoint() const
{
    std::istringstream is(value);
    is.imbue(std::locale("C"));
    double ret;
    is >> ret;
    return ret;
}

std::vector<uint8_t> Attribute::hexSequence() const
{
    std::vector<uint8_t> ret;
    if(value.length() > 2 && (value.substr(0, 2) == "0X" || value.substr(0, 2) == "0x") )
    {
        for(size_t i=2; i<=(value.length() - 2); i+=2)
        {
            unsigned val;
            std::stringstream ss(value.substr(i, 2));
            ss >> std::hex >> val;
            ret.push_back(val);
        }
    }
    return ret;
}

std::pair<std::size_t,std::size_t> Attribute::getByteRange() const
{
    std::size_t length = 0;
    std::size_t offset = 0;
    std::istringstream is(value);

    if(!is.eof())
    {
        is >> length;
        if(!is.eof())
        {
            char c = is.get();
            if(c == '@' && !is.eof())
                is >> offset;
        }
    }

    return std::make_pair(offset, length);
}

std::pair<int, int> Attribute::getResolution() const
{
    int w = 0, h = 0;

    std::istringstream is(value);
    if(!is.eof())
    {
        is >> w;
        if(!is.eof())
        {
            char c = is.get();
            if(c == 'x' && !is.eof())
                is >> h;
        }
    }

    return std::make_pair(w, h);
}

std::string Attribute::quotedString() const
{
    if(value.length() < 2)
        return "";

    std::istringstream is(value.substr(1, value.length() - 2));
    std::ostringstream os;

    char c;
    while(is.get(c))
    {
        if(c == '\\')
        {
            if(!is.get(c))
                break;
        }

        os << c;
    }

    return os.str();
}

Tag::Tag(int type_)
{
    type = type_;
}

Tag::~Tag()
{
}

int Tag::getType() const
{
    return type;
}

SingleValueTag::SingleValueTag(int type, const std::string &v)
    : Tag(type), attr("", v)
{
}

SingleValueTag::~SingleValueTag()
{

}

const Attribute &SingleValueTag::getValue() const
{
    return attr;
}

AttributesTag::AttributesTag(int type, const std::string &v) : Tag(type)
{
    parseAttributes(v);
}

AttributesTag::~AttributesTag()
{
    std::list<Attribute *>::const_iterator it;
    for(it = attributes.begin(); it != attributes.end(); ++it)
        delete *it;
}

const Attribute * AttributesTag::getAttributeByName(const char *name) const
{
    std::list<Attribute *>::const_iterator it;
    for(it = attributes.begin(); it != attributes.end(); ++it)
        if((*it)->name == name)
            return *it;

    return NULL;
}

void AttributesTag::addAttribute(Attribute *attr)
{
    attributes.push_back(attr);
}

void AttributesTag::parseAttributes(const std::string &field)
{
    std::istringstream iss(field);
    std::ostringstream oss;

    while(!iss.eof())
    {
        /* parse attribute name */
        while(!iss.eof())
        {
            char c = iss.peek();
            if((c >= 'A' && c <= 'Z') || c == '-')
            {
                oss.put((char)iss.get());
            }
            else if(c == '=')
            {
                iss.get();
                break;
            }
            else /* out of range */
                return;
        }

        std::string attrname = oss.str();
        oss.str("");

        /* parse attributes value */
        bool b_quoted = false;
        while(!iss.eof())
        {
            char c = iss.peek();
            if(c == '\\' && b_quoted)
            {
                iss.get();
            }
            else if(c == ',' && !b_quoted)
            {
                iss.get();
                break;
            }
            else if(c == '"')
            {
                b_quoted = !b_quoted;
            }

            if(!iss.eof())
                oss.put((char)iss.get());
        }

        std::string attrvalue = oss.str();
        oss.str("");

        Attribute *attribute = new (std::nothrow) Attribute(attrname, attrvalue);
        if(attribute)
            attributes.push_back(attribute);
    }
}

ValuesListTag::ValuesListTag(int type, const std::string &v) : AttributesTag(type, v)
{
    parseAttributes(v);
}

ValuesListTag::~ValuesListTag()
{
}

void ValuesListTag::parseAttributes(const std::string &field)
{
    std::size_t pos = field.find(',');
    if(pos != std::string::npos)
    {
        Attribute *attr = new (std::nothrow) Attribute("DURATION", field.substr(0, pos));
        if(attr)
            addAttribute(attr);

        attr = new (std::nothrow) Attribute("TITLE", field.substr(pos));
        if(attr)
            addAttribute(attr);
    }
}

Tag * TagFactory::createTagByName(const std::string &name, const std::string &value)
{
    struct
    {
        const char *psz;
        const int i;
    } const exttagmapping[] = {
        {"EXT-X-BYTERANGE",                 SingleValueTag::EXTXBYTERANGE},
        {"EXT-X-DISCONTINUITY",             Tag::EXTXDISCONTINUITY},
        {"EXT-X-KEY",                       AttributesTag::EXTXKEY},
        {"EXT-X-MAP",                       AttributesTag::EXTXMAP},
        {"EXT-X-PROGRAM-DATE-TIME",         SingleValueTag::EXTXPROGRAMDATETIME},
        {"EXT-X-TARGETDURATION",            SingleValueTag::EXTXTARGETDURATION},
        {"EXT-X-MEDIA-SEQUENCE",            SingleValueTag::EXTXMEDIASEQUENCE},
        {"EXT-X-DISCONTINUITY-SEQUENCE",    SingleValueTag::EXTXDISCONTINUITYSEQUENCE},
        {"EXT-X-ENDLIST",                   Tag::EXTXENDLIST},
        {"EXT-X-PLAYLIST-TYPE",             SingleValueTag::EXTXPLAYLISTTYPE},
        {"EXT-X-I-FRAMES-ONLY",             Tag::EXTXIFRAMESONLY},
        {"EXT-X-MEDIA",                     AttributesTag::EXTXMEDIA},
        {"EXT-X-STREAM-INF",                AttributesTag::EXTXSTREAMINF},
        {"EXTINF",                          ValuesListTag::EXTINF},
        {"",                                SingleValueTag::URI},
        {NULL,                              0},
    };

    for(int i=0; exttagmapping[i].psz; i++)
    {
        if(name != exttagmapping[i].psz)
            continue;

        switch(exttagmapping[i].i)
        {
        case Tag::EXTXDISCONTINUITY:
        case Tag::EXTXENDLIST:
        case Tag::EXTXIFRAMESONLY:
            return new (std::nothrow) Tag(exttagmapping[i].i);

        case SingleValueTag::URI:
        case SingleValueTag::EXTXVERSION:
        case SingleValueTag::EXTXBYTERANGE:
        case SingleValueTag::EXTXPROGRAMDATETIME:
        case SingleValueTag::EXTXTARGETDURATION:
        case SingleValueTag::EXTXMEDIASEQUENCE:
        case SingleValueTag::EXTXDISCONTINUITYSEQUENCE:
        case SingleValueTag::EXTXPLAYLISTTYPE:
            return new (std::nothrow) SingleValueTag(exttagmapping[i].i, value);

        case ValuesListTag::EXTINF:
            return new (std::nothrow) ValuesListTag(exttagmapping[i].i, value);

        case AttributesTag::EXTXKEY:
        case AttributesTag::EXTXMAP:
        case AttributesTag::EXTXMEDIA:
        case AttributesTag::EXTXSTREAMINF:
            return new (std::nothrow) AttributesTag(exttagmapping[i].i, value);
        }

    }

    return NULL;
}
<|endoftext|>


<commit_before>// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2019, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "test_precomp.hpp"

#ifdef HAVE_INF_ENGINE
#include <opencv2/core/utils/filesystem.hpp>

//
// Synchronize headers include statements with src/op_inf_engine.hpp
//
//#define INFERENCE_ENGINE_DEPRECATED  // turn off deprecation warnings from IE
//there is no way to suppress warnings from IE only at this moment, so we are forced to suppress warnings globally
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#ifdef _MSC_VER
#pragma warning(disable: 4996)  // was declared deprecated
#endif

#if defined(__GNUC__)
#pragma GCC visibility push(default)
#endif

#include <inference_engine.hpp>
#include <ie_icnn_network.hpp>
#include <ie_extension.h>

#if defined(__GNUC__)
#pragma GCC visibility pop
#endif

namespace opencv_test { namespace {

static void initDLDTDataPath()
{
#ifndef WINRT
    static bool initialized = false;
    if (!initialized)
    {
#if INF_ENGINE_RELEASE <= 2018050000
        const char* dldtTestDataPath = getenv("INTEL_CVSDK_DIR");
        if (dldtTestDataPath)
            cvtest::addDataSearchPath(dldtTestDataPath);
#else
        const char* omzDataPath = getenv("OPENCV_OPEN_MODEL_ZOO_DATA_PATH");
        if (omzDataPath)
            cvtest::addDataSearchPath(omzDataPath);
        const char* dnnDataPath = getenv("OPENCV_DNN_TEST_DATA_PATH");
        if (dnnDataPath)
            cvtest::addDataSearchPath(std::string(dnnDataPath) + "/omz_intel_models");
#endif
        initialized = true;
    }
#endif
}

using namespace cv;
using namespace cv::dnn;
using namespace InferenceEngine;

struct OpenVINOModelTestCaseInfo
{
    const char* modelPathFP32;
    const char* modelPathFP16;
};

static const std::map<std::string, OpenVINOModelTestCaseInfo>& getOpenVINOTestModels()
{
    static std::map<std::string, OpenVINOModelTestCaseInfo> g_models {
#if INF_ENGINE_RELEASE >= 2018050000
        // layout is defined by open_model_zoo/model_downloader
        // Downloaded using these parameters for Open Model Zoo downloader (2019R1):
        // ./downloader.py -o ${OPENCV_DNN_TEST_DATA_PATH}/omz_intel_models --cache_dir ${OPENCV_DNN_TEST_DATA_PATH}/.omz_cache/ \
        //     --name face-person-detection-retail-0002,face-person-detection-retail-0002-fp16,age-gender-recognition-retail-0013,age-gender-recognition-retail-0013-fp16,head-pose-estimation-adas-0001,head-pose-estimation-adas-0001-fp16,person-detection-retail-0002,person-detection-retail-0002-fp16,vehicle-detection-adas-0002,vehicle-detection-adas-0002-fp16
        { "age-gender-recognition-retail-0013", {
            "Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013",
            "Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013-fp16"
        }},
        { "face-person-detection-retail-0002", {
            "Retail/object_detection/face_pedestrian/rmnet-ssssd-2heads/0002/dldt/face-person-detection-retail-0002",
            "Retail/object_detection/face_pedestrian/rmnet-ssssd-2heads/0002/dldt/face-person-detection-retail-0002-fp16"
        }},
        { "head-pose-estimation-adas-0001", {
            "Transportation/object_attributes/headpose/vanilla_cnn/dldt/head-pose-estimation-adas-0001",
            "Transportation/object_attributes/headpose/vanilla_cnn/dldt/head-pose-estimation-adas-0001-fp16"
        }},
        { "person-detection-retail-0002", {
            "Retail/object_detection/pedestrian/hypernet-rfcn/0026/dldt/person-detection-retail-0002",
            "Retail/object_detection/pedestrian/hypernet-rfcn/0026/dldt/person-detection-retail-0002-fp16"
        }},
        { "vehicle-detection-adas-0002", {
            "Transportation/object_detection/vehicle/mobilenet-reduced-ssd/dldt/vehicle-detection-adas-0002",
            "Transportation/object_detection/vehicle/mobilenet-reduced-ssd/dldt/vehicle-detection-adas-0002-fp16"
        }},
#endif
#if INF_ENGINE_RELEASE >= 2020010000
        // Downloaded using these parameters for Open Model Zoo downloader (2020.1):
        // ./downloader.py -o ${OPENCV_DNN_TEST_DATA_PATH}/omz_intel_models --cache_dir ${OPENCV_DNN_TEST_DATA_PATH}/.omz_cache/ \
        //     --name person-detection-retail-0013
        { "person-detection-retail-0013", {  // IRv10
            "intel/person-detection-retail-0013/FP32/person-detection-retail-0013",
            "intel/person-detection-retail-0013/FP16/person-detection-retail-0013"
        }},
#endif
    };

    return g_models;
}

static const std::vector<std::string> getOpenVINOTestModelsList()
{
    std::vector<std::string> result;
    const std::map<std::string, OpenVINOModelTestCaseInfo>& models = getOpenVINOTestModels();
    for (const auto& it : models)
        result.push_back(it.first);
    return result;
}

static inline void genData(const InferenceEngine::TensorDesc& desc, Mat& m, Blob::Ptr& dataPtr)
{
    const std::vector<size_t>& dims = desc.getDims();
    m.create(std::vector<int>(dims.begin(), dims.end()), CV_32F);
    randu(m, -1, 1);

    dataPtr = make_shared_blob<float>(desc, (float*)m.data);
}

void runIE(Target target, const std::string& xmlPath, const std::string& binPath,
           std::map<std::string, cv::Mat>& inputsMap, std::map<std::string, cv::Mat>& outputsMap)
{
    SCOPED_TRACE("runIE");

    CNNNetReader reader;
    reader.ReadNetwork(xmlPath);
    reader.ReadWeights(binPath);

    CNNNetwork net = reader.getNetwork();

    std::string device_name;

#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GT(2019010000)
    Core ie;
#else
    InferenceEnginePluginPtr enginePtr;
    InferencePlugin plugin;
#endif
    ExecutableNetwork netExec;
    InferRequest infRequest;

    try
    {
        switch (target)
        {
            case DNN_TARGET_CPU:
                device_name = "CPU";
                break;
            case DNN_TARGET_OPENCL:
            case DNN_TARGET_OPENCL_FP16:
                device_name = "GPU";
                break;
            case DNN_TARGET_MYRIAD:
                device_name = "MYRIAD";
                break;
            case DNN_TARGET_FPGA:
                device_name = "FPGA";
                break;
            default:
                CV_Error(Error::StsNotImplemented, "Unknown target");
        };

#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LE(2019010000)
        auto dispatcher = InferenceEngine::PluginDispatcher({""});
        enginePtr = dispatcher.getPluginByDevice(device_name);
#endif
        if (target == DNN_TARGET_CPU || target == DNN_TARGET_FPGA)
        {
            std::string suffixes[] = {"_avx2", "_sse4", ""};
            bool haveFeature[] = {
                checkHardwareSupport(CPU_AVX2),
                checkHardwareSupport(CPU_SSE4_2),
                true
            };
            for (int i = 0; i < 3; ++i)
            {
                if (!haveFeature[i])
                    continue;
#ifdef _WIN32
                std::string libName = "cpu_extension" + suffixes[i] + ".dll";
#elif defined(__APPLE__)
                std::string libName = "libcpu_extension" + suffixes[i] + ".dylib";
#else
                std::string libName = "libcpu_extension" + suffixes[i] + ".so";
#endif  // _WIN32
                try
                {
                    IExtensionPtr extension = make_so_pointer<IExtension>(libName);
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GT(2019010000)
                    ie.AddExtension(extension, device_name);
#else
                    enginePtr->AddExtension(extension, 0);
#endif
                    break;
                }
                catch(...) {}
            }
            // Some of networks can work without a library of extra layers.
        }
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GT(2019010000)
        netExec = ie.LoadNetwork(net, device_name);
#else
        plugin = InferencePlugin(enginePtr);
        netExec = plugin.LoadNetwork(net, {});
#endif
        infRequest = netExec.CreateInferRequest();
    }
    catch (const std::exception& ex)
    {
        CV_Error(Error::StsAssert, format("Failed to initialize Inference Engine backend: %s", ex.what()));
    }

    // Fill input blobs.
    inputsMap.clear();
    BlobMap inputBlobs;
    for (auto& it : net.getInputsInfo())
    {
        genData(it.second->getTensorDesc(), inputsMap[it.first], inputBlobs[it.first]);
    }
    infRequest.SetInput(inputBlobs);

    // Fill output blobs.
    outputsMap.clear();
    BlobMap outputBlobs;
    for (auto& it : net.getOutputsInfo())
    {
        genData(it.second->getTensorDesc(), outputsMap[it.first], outputBlobs[it.first]);
    }
    infRequest.SetOutput(outputBlobs);

    infRequest.Infer();
}

void runCV(Backend backendId, Target targetId, const std::string& xmlPath, const std::string& binPath,
           const std::map<std::string, cv::Mat>& inputsMap,
           std::map<std::string, cv::Mat>& outputsMap)
{
    SCOPED_TRACE("runOCV");

    Net net = readNet(xmlPath, binPath);
    for (auto& it : inputsMap)
        net.setInput(it.second, it.first);

    net.setPreferableBackend(backendId);
    net.setPreferableTarget(targetId);

    std::vector<String> outNames = net.getUnconnectedOutLayersNames();
    std::vector<Mat> outs;
    net.forward(outs, outNames);

    outputsMap.clear();
    EXPECT_EQ(outs.size(), outNames.size());
    for (int i = 0; i < outs.size(); ++i)
    {
        EXPECT_TRUE(outputsMap.insert({outNames[i], outs[i]}).second);
    }
}

typedef TestWithParam<tuple< tuple<Backend, Target>, std::string> > DNNTestOpenVINO;
TEST_P(DNNTestOpenVINO, models)
{
    initDLDTDataPath();

    const Backend backendId = get<0>(get<0>(GetParam()));
    const Target targetId = get<1>(get<0>(GetParam()));
    std::string modelName = get<1>(GetParam());

    ASSERT_FALSE(backendId != DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && backendId != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) <<
        "Inference Engine backend is required";

#if INF_ENGINE_VER_MAJOR_GE(2020020000)
    if (targetId == DNN_TARGET_MYRIAD && backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
    {
        if (modelName == "person-detection-retail-0013")  // IRv10
            applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER, CV_TEST_TAG_DNN_SKIP_IE_VERSION);
    }
#endif

    if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
        setInferenceEngineBackendType(CV_DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_API);
    else if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
        setInferenceEngineBackendType(CV_DNN_BACKEND_INFERENCE_ENGINE_NGRAPH);
    else
        FAIL() << "Unknown backendId";

    bool isFP16 = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD);

    const std::map<std::string, OpenVINOModelTestCaseInfo>& models = getOpenVINOTestModels();
    const auto it = models.find(modelName);
    ASSERT_TRUE(it != models.end()) << modelName;
    OpenVINOModelTestCaseInfo modelInfo = it->second;
    std::string modelPath = isFP16 ? modelInfo.modelPathFP16 : modelInfo.modelPathFP32;

    std::string xmlPath = findDataFile(modelPath + ".xml", false);
    std::string binPath = findDataFile(modelPath + ".bin", false);

    std::map<std::string, cv::Mat> inputsMap;
    std::map<std::string, cv::Mat> ieOutputsMap, cvOutputsMap;
    // Single Myriad device cannot be shared across multiple processes.
    if (targetId == DNN_TARGET_MYRIAD)
        resetMyriadDevice();
    EXPECT_NO_THROW(runIE(targetId, xmlPath, binPath, inputsMap, ieOutputsMap)) << "runIE";
    EXPECT_NO_THROW(runCV(backendId, targetId, xmlPath, binPath, inputsMap, cvOutputsMap)) << "runCV";

    double eps = 0;
#if INF_ENGINE_VER_MAJOR_GE(2020010000)
    if (targetId == DNN_TARGET_CPU && checkHardwareSupport(CV_CPU_AVX_512F))
        eps = 1e-5;
#endif

    EXPECT_EQ(ieOutputsMap.size(), cvOutputsMap.size());
    for (auto& srcIt : ieOutputsMap)
    {
        auto dstIt = cvOutputsMap.find(srcIt.first);
        CV_Assert(dstIt != cvOutputsMap.end());
        double normInf = cvtest::norm(srcIt.second, dstIt->second, cv::NORM_INF);
        EXPECT_LE(normInf, eps) << "output=" << srcIt.first;
    }
}

INSTANTIATE_TEST_CASE_P(/**/,
    DNNTestOpenVINO,
    Combine(dnnBackendsAndTargetsIE(),
            testing::ValuesIn(getOpenVINOTestModelsList())
    )
);

}}
#endif  // HAVE_INF_ENGINE
<commit_msg>Conditional compilation for network reader<commit_after>// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2019, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "test_precomp.hpp"

#ifdef HAVE_INF_ENGINE
#include <opencv2/core/utils/filesystem.hpp>

//
// Synchronize headers include statements with src/op_inf_engine.hpp
//
//#define INFERENCE_ENGINE_DEPRECATED  // turn off deprecation warnings from IE
//there is no way to suppress warnings from IE only at this moment, so we are forced to suppress warnings globally
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#ifdef _MSC_VER
#pragma warning(disable: 4996)  // was declared deprecated
#endif

#if defined(__GNUC__)
#pragma GCC visibility push(default)
#endif

#include <inference_engine.hpp>
#include <ie_icnn_network.hpp>
#include <ie_extension.h>

#if defined(__GNUC__)
#pragma GCC visibility pop
#endif

namespace opencv_test { namespace {

static void initDLDTDataPath()
{
#ifndef WINRT
    static bool initialized = false;
    if (!initialized)
    {
#if INF_ENGINE_RELEASE <= 2018050000
        const char* dldtTestDataPath = getenv("INTEL_CVSDK_DIR");
        if (dldtTestDataPath)
            cvtest::addDataSearchPath(dldtTestDataPath);
#else
        const char* omzDataPath = getenv("OPENCV_OPEN_MODEL_ZOO_DATA_PATH");
        if (omzDataPath)
            cvtest::addDataSearchPath(omzDataPath);
        const char* dnnDataPath = getenv("OPENCV_DNN_TEST_DATA_PATH");
        if (dnnDataPath)
            cvtest::addDataSearchPath(std::string(dnnDataPath) + "/omz_intel_models");
#endif
        initialized = true;
    }
#endif
}

using namespace cv;
using namespace cv::dnn;
using namespace InferenceEngine;

struct OpenVINOModelTestCaseInfo
{
    const char* modelPathFP32;
    const char* modelPathFP16;
};

static const std::map<std::string, OpenVINOModelTestCaseInfo>& getOpenVINOTestModels()
{
    static std::map<std::string, OpenVINOModelTestCaseInfo> g_models {
#if INF_ENGINE_RELEASE >= 2018050000
        // layout is defined by open_model_zoo/model_downloader
        // Downloaded using these parameters for Open Model Zoo downloader (2019R1):
        // ./downloader.py -o ${OPENCV_DNN_TEST_DATA_PATH}/omz_intel_models --cache_dir ${OPENCV_DNN_TEST_DATA_PATH}/.omz_cache/ \
        //     --name face-person-detection-retail-0002,face-person-detection-retail-0002-fp16,age-gender-recognition-retail-0013,age-gender-recognition-retail-0013-fp16,head-pose-estimation-adas-0001,head-pose-estimation-adas-0001-fp16,person-detection-retail-0002,person-detection-retail-0002-fp16,vehicle-detection-adas-0002,vehicle-detection-adas-0002-fp16
        { "age-gender-recognition-retail-0013", {
            "Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013",
            "Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013-fp16"
        }},
        { "face-person-detection-retail-0002", {
            "Retail/object_detection/face_pedestrian/rmnet-ssssd-2heads/0002/dldt/face-person-detection-retail-0002",
            "Retail/object_detection/face_pedestrian/rmnet-ssssd-2heads/0002/dldt/face-person-detection-retail-0002-fp16"
        }},
        { "head-pose-estimation-adas-0001", {
            "Transportation/object_attributes/headpose/vanilla_cnn/dldt/head-pose-estimation-adas-0001",
            "Transportation/object_attributes/headpose/vanilla_cnn/dldt/head-pose-estimation-adas-0001-fp16"
        }},
        { "person-detection-retail-0002", {
            "Retail/object_detection/pedestrian/hypernet-rfcn/0026/dldt/person-detection-retail-0002",
            "Retail/object_detection/pedestrian/hypernet-rfcn/0026/dldt/person-detection-retail-0002-fp16"
        }},
        { "vehicle-detection-adas-0002", {
            "Transportation/object_detection/vehicle/mobilenet-reduced-ssd/dldt/vehicle-detection-adas-0002",
            "Transportation/object_detection/vehicle/mobilenet-reduced-ssd/dldt/vehicle-detection-adas-0002-fp16"
        }},
#endif
#if INF_ENGINE_RELEASE >= 2020010000
        // Downloaded using these parameters for Open Model Zoo downloader (2020.1):
        // ./downloader.py -o ${OPENCV_DNN_TEST_DATA_PATH}/omz_intel_models --cache_dir ${OPENCV_DNN_TEST_DATA_PATH}/.omz_cache/ \
        //     --name person-detection-retail-0013
        { "person-detection-retail-0013", {  // IRv10
            "intel/person-detection-retail-0013/FP32/person-detection-retail-0013",
            "intel/person-detection-retail-0013/FP16/person-detection-retail-0013"
        }},
#endif
    };

    return g_models;
}

static const std::vector<std::string> getOpenVINOTestModelsList()
{
    std::vector<std::string> result;
    const std::map<std::string, OpenVINOModelTestCaseInfo>& models = getOpenVINOTestModels();
    for (const auto& it : models)
        result.push_back(it.first);
    return result;
}

static inline void genData(const InferenceEngine::TensorDesc& desc, Mat& m, Blob::Ptr& dataPtr)
{
    const std::vector<size_t>& dims = desc.getDims();
    m.create(std::vector<int>(dims.begin(), dims.end()), CV_32F);
    randu(m, -1, 1);

    dataPtr = make_shared_blob<float>(desc, (float*)m.data);
}

void runIE(Target target, const std::string& xmlPath, const std::string& binPath,
           std::map<std::string, cv::Mat>& inputsMap, std::map<std::string, cv::Mat>& outputsMap)
{
    SCOPED_TRACE("runIE");

    std::string device_name;

#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GT(2019010000)
    Core ie;
#else
    InferenceEnginePluginPtr enginePtr;
    InferencePlugin plugin;
#endif

#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GT(2019030000)
    CNNNetwork net = ie.ReadNetwork(xmlPath, binPath);
#else
    CNNNetReader reader;
    reader.ReadNetwork(xmlPath);
    reader.ReadWeights(binPath);

    CNNNetwork net = reader.getNetwork();
#endif

    ExecutableNetwork netExec;
    InferRequest infRequest;

    try
    {
        switch (target)
        {
            case DNN_TARGET_CPU:
                device_name = "CPU";
                break;
            case DNN_TARGET_OPENCL:
            case DNN_TARGET_OPENCL_FP16:
                device_name = "GPU";
                break;
            case DNN_TARGET_MYRIAD:
                device_name = "MYRIAD";
                break;
            case DNN_TARGET_FPGA:
                device_name = "FPGA";
                break;
            default:
                CV_Error(Error::StsNotImplemented, "Unknown target");
        };

#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_LE(2019010000)
        auto dispatcher = InferenceEngine::PluginDispatcher({""});
        enginePtr = dispatcher.getPluginByDevice(device_name);
#endif
        if (target == DNN_TARGET_CPU || target == DNN_TARGET_FPGA)
        {
            std::string suffixes[] = {"_avx2", "_sse4", ""};
            bool haveFeature[] = {
                checkHardwareSupport(CPU_AVX2),
                checkHardwareSupport(CPU_SSE4_2),
                true
            };
            for (int i = 0; i < 3; ++i)
            {
                if (!haveFeature[i])
                    continue;
#ifdef _WIN32
                std::string libName = "cpu_extension" + suffixes[i] + ".dll";
#elif defined(__APPLE__)
                std::string libName = "libcpu_extension" + suffixes[i] + ".dylib";
#else
                std::string libName = "libcpu_extension" + suffixes[i] + ".so";
#endif  // _WIN32
                try
                {
                    IExtensionPtr extension = make_so_pointer<IExtension>(libName);
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GT(2019010000)
                    ie.AddExtension(extension, device_name);
#else
                    enginePtr->AddExtension(extension, 0);
#endif
                    break;
                }
                catch(...) {}
            }
            // Some of networks can work without a library of extra layers.
        }
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GT(2019010000)
        netExec = ie.LoadNetwork(net, device_name);
#else
        plugin = InferencePlugin(enginePtr);
        netExec = plugin.LoadNetwork(net, {});
#endif
        infRequest = netExec.CreateInferRequest();
    }
    catch (const std::exception& ex)
    {
        CV_Error(Error::StsAssert, format("Failed to initialize Inference Engine backend: %s", ex.what()));
    }

    // Fill input blobs.
    inputsMap.clear();
    BlobMap inputBlobs;
    for (auto& it : net.getInputsInfo())
    {
        genData(it.second->getTensorDesc(), inputsMap[it.first], inputBlobs[it.first]);
    }
    infRequest.SetInput(inputBlobs);

    // Fill output blobs.
    outputsMap.clear();
    BlobMap outputBlobs;
    for (auto& it : net.getOutputsInfo())
    {
        genData(it.second->getTensorDesc(), outputsMap[it.first], outputBlobs[it.first]);
    }
    infRequest.SetOutput(outputBlobs);

    infRequest.Infer();
}

void runCV(Backend backendId, Target targetId, const std::string& xmlPath, const std::string& binPath,
           const std::map<std::string, cv::Mat>& inputsMap,
           std::map<std::string, cv::Mat>& outputsMap)
{
    SCOPED_TRACE("runOCV");

    Net net = readNet(xmlPath, binPath);
    for (auto& it : inputsMap)
        net.setInput(it.second, it.first);

    net.setPreferableBackend(backendId);
    net.setPreferableTarget(targetId);

    std::vector<String> outNames = net.getUnconnectedOutLayersNames();
    std::vector<Mat> outs;
    net.forward(outs, outNames);

    outputsMap.clear();
    EXPECT_EQ(outs.size(), outNames.size());
    for (int i = 0; i < outs.size(); ++i)
    {
        EXPECT_TRUE(outputsMap.insert({outNames[i], outs[i]}).second);
    }
}

typedef TestWithParam<tuple< tuple<Backend, Target>, std::string> > DNNTestOpenVINO;
TEST_P(DNNTestOpenVINO, models)
{
    initDLDTDataPath();

    const Backend backendId = get<0>(get<0>(GetParam()));
    const Target targetId = get<1>(get<0>(GetParam()));
    std::string modelName = get<1>(GetParam());

    ASSERT_FALSE(backendId != DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && backendId != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) <<
        "Inference Engine backend is required";

#if INF_ENGINE_VER_MAJOR_GE(2020020000)
    if (targetId == DNN_TARGET_MYRIAD && backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
    {
        if (modelName == "person-detection-retail-0013")  // IRv10
            applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER, CV_TEST_TAG_DNN_SKIP_IE_VERSION);
    }
#endif

    if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
        setInferenceEngineBackendType(CV_DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_API);
    else if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
        setInferenceEngineBackendType(CV_DNN_BACKEND_INFERENCE_ENGINE_NGRAPH);
    else
        FAIL() << "Unknown backendId";

    bool isFP16 = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD);

    const std::map<std::string, OpenVINOModelTestCaseInfo>& models = getOpenVINOTestModels();
    const auto it = models.find(modelName);
    ASSERT_TRUE(it != models.end()) << modelName;
    OpenVINOModelTestCaseInfo modelInfo = it->second;
    std::string modelPath = isFP16 ? modelInfo.modelPathFP16 : modelInfo.modelPathFP32;

    std::string xmlPath = findDataFile(modelPath + ".xml", false);
    std::string binPath = findDataFile(modelPath + ".bin", false);

    std::map<std::string, cv::Mat> inputsMap;
    std::map<std::string, cv::Mat> ieOutputsMap, cvOutputsMap;
    // Single Myriad device cannot be shared across multiple processes.
    if (targetId == DNN_TARGET_MYRIAD)
        resetMyriadDevice();
    EXPECT_NO_THROW(runIE(targetId, xmlPath, binPath, inputsMap, ieOutputsMap)) << "runIE";
    EXPECT_NO_THROW(runCV(backendId, targetId, xmlPath, binPath, inputsMap, cvOutputsMap)) << "runCV";

    double eps = 0;
#if INF_ENGINE_VER_MAJOR_GE(2020010000)
    if (targetId == DNN_TARGET_CPU && checkHardwareSupport(CV_CPU_AVX_512F))
        eps = 1e-5;
#endif

    EXPECT_EQ(ieOutputsMap.size(), cvOutputsMap.size());
    for (auto& srcIt : ieOutputsMap)
    {
        auto dstIt = cvOutputsMap.find(srcIt.first);
        CV_Assert(dstIt != cvOutputsMap.end());
        double normInf = cvtest::norm(srcIt.second, dstIt->second, cv::NORM_INF);
        EXPECT_LE(normInf, eps) << "output=" << srcIt.first;
    }
}

INSTANTIATE_TEST_CASE_P(/**/,
    DNNTestOpenVINO,
    Combine(dnnBackendsAndTargetsIE(),
            testing::ValuesIn(getOpenVINOTestModelsList())
    )
);

}}
#endif  // HAVE_INF_ENGINE
<|endoftext|>