1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
|
#ifndef TAKANE_UTILS_SUMMARIZED_EXPERIMENT_HPP
#define TAKANE_UTILS_SUMMARIZED_EXPERIMENT_HPP
#include "millijson/millijson.hpp"
#include "utils_json.hpp"
#include <unordered_set>
#include <string>
#include <stdexcept>
#include <filesystem>
#include <cmath>
namespace takane {
namespace internal_summarized_experiment {
inline std::pair<size_t, size_t> extract_dimensions_json(const internal_json::JsonObjectMap& semap, const std::string& type) {
size_t num_rows = 0, num_cols = 0;
auto dIt = semap.find("dimensions");
if (dIt == semap.end()) {
throw std::runtime_error("expected a '" + type + ".dimensions' property");
}
const auto& dims = dIt->second;
if (dims->type() != millijson::ARRAY) {
throw std::runtime_error("expected '" + type + ".dimensions' to be an array");
}
auto dptr = reinterpret_cast<const millijson::Array*>(dims.get());
if (dptr->values.size() != 2) {
throw std::runtime_error("expected '" + type + ".dimensions' to be an array of length 2");
}
size_t counter = 0;
for (const auto& x : dptr->values) {
if (x->type() != millijson::NUMBER) {
throw std::runtime_error("expected '" + type + ".dimensions' to be an array of numbers");
}
auto val = reinterpret_cast<const millijson::Number*>(x.get())->value;
if (val < 0 || std::floor(val) != val) {
throw std::runtime_error("expected '" + type + ".dimensions' to contain non-negative integers");
}
if (counter == 0) {
num_rows = val;
} else {
num_cols = val;
}
++counter;
}
return std::make_pair(num_rows, num_cols);
}
inline void check_names_json(const std::filesystem::path& dir, std::unordered_set<std::string>& present) try {
auto parsed = internal_json::parse_file(dir / "names.json");
if (parsed->type() != millijson::ARRAY) {
throw std::runtime_error("expected an array");
}
auto aptr = reinterpret_cast<const millijson::Array*>(parsed.get());
size_t number = aptr->values.size();
present.reserve(number);
for (size_t i = 0; i < number; ++i) {
auto eptr = aptr->values[i];
if (eptr->type() != millijson::STRING) {
throw std::runtime_error("expected an array of strings");
}
auto nptr = reinterpret_cast<const millijson::String*>(eptr.get());
auto name = nptr->value;
if (name.empty()) {
throw std::runtime_error("name should not be an empty string");
}
if (present.find(name) != present.end()) {
throw std::runtime_error("detected duplicated name '" + name + "'");
}
present.insert(std::move(name));
}
} catch (std::exception& e) {
throw std::runtime_error("invalid '" + dir.string() + "/names.json' file; " + std::string(e.what()));
}
inline size_t check_names_json(const std::filesystem::path& dir) {
std::unordered_set<std::string> present;
check_names_json(dir, present);
return present.size();
}
}
}
#endif
|