5#ifndef LINE_REG_API_JSON_H
6#define LINE_REG_API_JSON_H
63using Json = nlohmann::json;
78 for (
int prec = 1; prec <= 17; ++prec) {
79 std::snprintf(buf,
sizeof(buf),
"%.*g", prec, v);
80 if (std::strtod(buf,
nullptr) == v)
return std::string(buf);
82 std::snprintf(buf,
sizeof(buf),
"%.17g", v);
83 return std::string(buf);
88 for (
unsigned k = 0; k < e; ++k) p *= 10;
98 const std::size_t slash = text.find(
'/');
99 if (slash != std::string::npos)
104 const std::size_t n = text.size();
105 while (i < n && (text[i] ==
' ' || text[i] ==
'\t')) ++i;
106 bool negative =
false;
107 if (i < n && (text[i] ==
'+' || text[i] ==
'-')) negative = (text[i++] ==
'-');
110 long frac_digits = 0;
112 while (i < n && text[i] >=
'0' && text[i] <=
'9') {
116 if (i < n && text[i] ==
'.') {
118 while (i < n && text[i] >=
'0' && text[i] <=
'9') {
124 if (!any)
throw InputError(
"not a decimal number: '" + text +
"'");
127 if (i < n && (text[i] ==
'e' || text[i] ==
'E')) {
130 if (i < n && (text[i] ==
'+' || text[i] ==
'-')) eneg = (text[i++] ==
'-');
132 while (i < n && text[i] >=
'0' && text[i] <=
'9') edig += text[i++];
133 if (edig.empty())
throw InputError(
"malformed exponent in '" + text +
"'");
134 exponent = std::strtol(edig.c_str(),
nullptr, 10);
135 if (eneg) exponent = -exponent;
137 while (i < n && (text[i] ==
' ' || text[i] ==
'\t')) ++i;
138 if (i != n)
throw InputError(
"trailing characters in number '" + text +
"'");
141 std::size_t first = digits.find_first_not_of(
'0');
142 digits = (first == std::string::npos) ? std::string(
"0") : digits.substr(first);
143 const BigInt mantissa(digits);
145 const long scale = exponent - frac_digits;
155 const std::size_t slash = text.find(
'/');
156 if (slash != std::string::npos)
159 const char* s = text.c_str();
161 const double v = std::strtod(s, &end);
162 if (end == s)
throw InputError(
"not a decimal number: '" + text +
"'");
163 while (*end ==
' ' || *end ==
'\t') ++end;
164 if (*end !=
'\0')
throw InputError(
"trailing characters in number '" + text +
"'");
188 const std::size_t slash = text.find(
'/');
189 if (slash != std::string::npos)
200 if (j.is_string())
return j.get<std::string>();
201 if (j.is_number_integer())
return std::to_string(j.get<
long long>());
202 if (j.is_number_unsigned())
return std::to_string(j.get<
unsigned long long>());
204 if (j.is_boolean())
return j.get<
bool>() ?
"1" :
"0";
205 throw InputError(where +
": expected a number or a numeric string, got " +
206 std::string(j.type_name()));
230 if (j[0].is_array()) {
231 const std::size_t rows = j.size();
232 const std::size_t cols = j[0].size();
233 for (std::size_t i = 0; i < rows; ++i) {
234 if (!j[i].is_array())
235 throw InputError(where +
": row " + std::to_string(i) +
" is not an array");
236 if (j[i].size() != cols)
237 throw InputError(where +
": row " + std::to_string(i) +
" has " +
238 std::to_string(j[i].size()) +
" entries, row 0 has " +
239 std::to_string(cols));
243 for (std::size_t i = 0; i < rows; ++i)
244 for (std::size_t k = 0; k < cols; ++k)
257 if (m.
empty())
return std::vector<T>();
259 throw InputError(where +
": expected a vector, got a " + std::to_string(m.
rows()) +
"x" +
260 std::to_string(m.
cols()) +
" matrix");
261 std::vector<T> v(m.
size());
262 for (std::size_t k = 0; k < m.
size(); ++k) v[k] = m[k];
268 std::vector<Json> flat;
272 for (
const Json& e : j) {
274 for (
const Json& f : e) flat.push_back(f);
280 v.reserve(flat.size());
281 for (
const Json& e : flat) {
284 if (denominator(r) != 1)
285 throw InputError(where +
": expected an integer, got " + text);
286 const double d =
static_cast<double>(r);
287 if (d > 2147483647.0 || d < -2147483648.0)
288 throw InputError(where +
": integer out of range: " + text);
289 v.push_back(
static_cast<int>(d));
322 j[
"dec"] = v.str(
static_cast<std::streamsize
>(D), std::ios_base::scientific);
334 Json a = Json::array();
341 Json a = Json::array();
342 for (std::size_t i = 0; i < m.
rows(); ++i) {
343 Json row = Json::array();
344 for (std::size_t k = 0; k < m.
cols(); ++k) row.push_back(
encode_scalar(m(i, k)));
351 Json a = Json::array();
352 for (
int x : v) a.push_back(x);
359 Json a = Json::array();
367 Json a = Json::array();
368 for (
const std::vector<T>& x : v) a.push_back(
encode_vector(x));
393 Args(
const Json& j, std::string function) : j_(j), fn_(std::move(function)) {
395 throw InputError(fn_ +
": the argument JSON must be an object keyed by the MATLAB "
396 "parameter names, got " +
397 std::string(j_.type_name()));
400 bool has(
const char* key)
const {
return j_.find(key) != j_.end(); }
405 auto it = j_.find(key);
406 if (it == j_.end())
throw InputError(fn_ +
": missing required argument '" + key +
"'");
413 auto it = j_.find(key);
414 return it == j_.end() ? nullptr : &*it;
446 std::vector<Matrix<T> >
matrices(
const char* key) {
448 const std::string where = fn_ +
": " + key;
450 throw InputError(where +
": expected a list of matrices, got " +
451 std::string(j.type_name()));
452 std::vector<Matrix<T> > out;
453 for (std::size_t i = 0; i < j.size(); ++i)
461 if (!
has(key))
return std::vector<Matrix<T> >();
475 std::size_t
count(
const char* key) {
478 throw InputError(fn_ +
": '" + key +
"' is a count and cannot be negative, got " +
480 return static_cast<std::size_t
>(v);
483 std::size_t
count(
const char* key, std::size_t fallback) {
492 unsigned uinteger(
const char* key) {
return static_cast<unsigned>(
count(key)); }
494 unsigned uinteger(
const char* key,
unsigned fallback) {
495 return static_cast<unsigned>(
count(key,
static_cast<std::size_t
>(fallback)));
500 if (got.size() != 1)
throw InputError(fn_ +
": '" + key +
"' must be a single integer");
505 bool boolean(
const char* key,
bool fallback) {
507 if (!v)
return fallback;
508 if (v->is_boolean())
return v->get<
bool>();
510 if (got.size() != 1 || (got[0] != 0 && got[0] != 1))
511 throw InputError(fn_ +
": '" + key +
"' must be true, false, 0 or 1");
516 std::string
text(
const char* key,
const std::string& fallback) {
518 if (!v)
return fallback;
520 throw InputError(fn_ +
": '" + key +
"' must be a string, got " +
521 std::string(v->type_name()));
522 return v->get<std::string>();
525 std::vector<int>
ints(
const char* key) {
536 if (!v)
return fallback;
539 throw InputError(fn_ +
": '" + key +
"' must be a single integer");
544 T
scalar(
const char* key,
const T& fallback) {
555 if (j_.find(key) != j_.end())
556 throw UnsupportedError(fn_ +
": argument '" + key +
"' cannot cross the JSON API "
562 for (
auto it = j_.begin(); it != j_.end(); ++it) {
563 if (seen_.find(it.key()) != seen_.end())
continue;
564 std::string accepted;
565 for (
const std::string& k : seen_) {
566 if (!accepted.empty()) accepted +=
", ";
569 throw InputError(fn_ +
": unknown argument '" + it.key() +
"'; " + fn_ +
570 " accepts: " + accepted);
577 std::set<std::string> seen_;
UnsupportedError(const std::string &what)
unsigned uinteger(const char *key)
The same as count(), for the arguments the port declares unsigned.
std::size_t count(const char *key)
A required nonnegative count.
Matrix< T > matrix(const char *key)
int integer(const char *key, int fallback)
std::string text(const char *key, const std::string &fallback)
A string-valued option, e.g.
const Json * opt(const char *key)
Optional argument; returns nullptr when absent.
std::vector< T > vector_or_empty(const char *key)
unsigned uinteger(const char *key, unsigned fallback)
void unsupported(const char *key, const std::string &why)
Refuse an argument the JSON boundary cannot carry, naming it, rather than proceeding as if it had not...
std::vector< Matrix< T > > matrices(const char *key)
Required list of matrices, e.g.
std::vector< int > ints(const char *key)
const Json & get(const char *key)
Required argument; throws when absent.
bool has(const char *key) const
int required_integer(const char *key)
Matrix< T > matrix_or_empty(const char *key)
T scalar(const char *key, const T &fallback)
std::size_t count(const char *key, std::size_t fallback)
std::vector< int > ints_or_empty(const char *key)
std::vector< Matrix< T > > matrices_or_empty(const char *key)
std::vector< T > vector(const char *key)
Required vector argument; absent is an error, not an empty vector.
Args(const Json &j, std::string function)
T number(const char *key)
Required numeric argument, at the arithmetic of the call.
bool boolean(const char *key, bool fallback)
A flag.
The exception types the port throws.
Dense matrix and non-owning view.
Json encode_vectors(const std::vector< std::vector< T > > &v)
A list of vectors, the shape a per-class or per-segment result returns.
double double_from_decimal(const std::string &text)
double value of a decimal literal, including the "a/b" fraction form.
Matrix< T > matrix_from_json(const Json &j, const std::string &where)
A matrix from a JSON value: 2-D array as rows, 1-D array as a row vector, scalar as 1x1,...
std::vector< int > int_vector_from_json(const Json &j, const std::string &where)
An integer vector; a non-integral entry is an error, never a truncation.
Json encode_matrices(const std::vector< Matrix< T > > &v)
A list of matrices, the shape the MMAP/BMAP families return as {D0,D1,...}.
Rational rational_from_decimal(const std::string &text)
Exact value of a decimal literal, with no rounding anywhere: sign, digits, optional fraction and opti...
BigInt pow10_bigint(unsigned e)
T number_from_json(const Json &j, const std::string &where)
std::vector< T > vector_from_json(const Json &j, const std::string &where)
A flat numeric vector: 1-D array, or a 1-row / 1-column 2-D array.
Json encode_vector(const std::vector< T > &v)
Json encode_matrix(const Matrix< T > &m)
std::string decimal_text(const Json &j, const std::string &where)
The decimal literal behind a JSON scalar, per the policy in the file header.
std::string shortest_decimal(double v)
The shortest decimal literal that round-trips to v.
Json encode_scalar(const T &v)
Json encode_count(std::size_t n)
A count.
Json encode_ints(const std::vector< int > &v)
boost::multiprecision::cpp_int BigInt
boost::multiprecision::number< boost::multiprecision::cpp_rational_backend, boost::multiprecision::et_off > Rational
boost::multiprecision::number< boost::multiprecision::cpp_bin_float< Digits10 > > Real
Number-type abstraction for the templated API port.
static Json encode(const Rational &v)
static Json encode(const Real< D > &v)
static Json encode(const double &v)
static Rational parse(const std::string &text)
static Real< D > parse(const std::string &text)
static double parse(const std::string &text)