LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
api_json.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_REG_API_JSON_H
6#define LINE_REG_API_JSON_H
7
8/**
9 * @file
10 * @ingroup line_reg
11 * The one conversion policy between JSON and the templated API layer.
12 *
13 * Every host boundary that carries API arguments and results as JSON goes
14 * through this header: the --api path of the CLI today, a pybind11 or MEX
15 * gateway later. Two gateways that each invent their own number conversion
16 * will disagree the first time a caller writes 0.6, so the policy is stated
17 * once, here, and both sides share it.
18 *
19 * ARGUMENTS. The object's keys are the MATLAB parameter names verbatim
20 * ({"L": [[0.6,0.4]], "N": [2,1], "Z": [1,0.5]}). A 2-D array is a matrix,
21 * row-major with the outer index the row; a 1-D array is a row vector; a bare
22 * number is a 1x1 scalar. An unrecognised key is an error, never ignored: a
23 * misspelt argument that silently takes its default is a wrong answer.
24 *
25 * NUMBERS. A JSON number is interpreted as the SHORTEST DECIMAL LITERAL that
26 * round-trips to it, and that decimal is what the arithmetic sees. So 0.6
27 * becomes the rational 3/5 in exact arithmetic, not the dyadic
28 * 5404319552844595/9007199254740992 that double-to-rational conversion would
29 * give. This is the only reading under which "exact" means what a caller
30 * writing 0.6 intends, and it is deterministic because the shortest
31 * round-tripping decimal of a double is unique. A caller who wants a value
32 * that has no short decimal form passes a STRING: "1/3" and "0.3333" are both
33 * accepted, and the string is taken literally at every arithmetic.
34 *
35 * RESULTS. A value of the algorithm's number type T encodes as
36 * double -> the bare JSON number
37 * exact -> {"double": `<approx>`, "num": "`<decimal>`", "den": "`<decimal>`"}
38 * real:`<D>` -> {"double": `<approx>`, "dec": "`<decimal string>`"}
39 * Exact numerators and denominators overflow every integer type, so they cross
40 * as decimal strings; the host rebuilds them with sym(num)/sym(den) or
41 * fractions.Fraction. A value that is a C++ double in the algorithm itself
42 * regardless of T -- lG is the only one in the port -- always encodes as a
43 * bare JSON number, because there is no exact value to report: lG is computed
44 * exponent-safely as log(num) - log(den) and is finite where G is not
45 * representable at all. Never exponentiate it back.
46 */
47
48#include <cmath>
49#include <cstdio>
50#include <cstdlib>
51#include <set>
52#include <string>
53#include <vector>
54
55#include "json.hpp"
56#include "line/num/number.h"
57#include "line/util/error.h"
58#include "line/util/matrix.h"
59
60namespace line {
61namespace reg {
62
63using Json = nlohmann::json;
64
65// ---------------------------------------------------------------------------
66// Decimal literal recovery and exact decimal parsing
67// ---------------------------------------------------------------------------
68
69/**
70 * The shortest decimal literal that round-trips to v. nlohmann::json does not
71 * retain the text of a number, so this reconstructs the literal a human wrote:
72 * for every double that came from a short decimal in the file, the reconstruction
73 * IS that decimal, because a double has at most one shortest round-tripping
74 * representation.
75 */
76inline std::string shortest_decimal(double v) {
77 char buf[64];
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);
81 }
82 std::snprintf(buf, sizeof(buf), "%.17g", v);
83 return std::string(buf);
84}
85
86inline BigInt pow10_bigint(unsigned e) {
87 BigInt p = 1;
88 for (unsigned k = 0; k < e; ++k) p *= 10;
89 return p;
90}
91
92/**
93 * Exact value of a decimal literal, with no rounding anywhere: sign, digits,
94 * optional fraction and optional exponent are read symbolically and assembled
95 * as numerator over a power of ten.
96 */
97inline Rational rational_from_decimal(const std::string& text) {
98 const std::size_t slash = text.find('/');
99 if (slash != std::string::npos)
100 return rational_from_decimal(text.substr(0, slash)) /
101 rational_from_decimal(text.substr(slash + 1));
102
103 std::size_t i = 0;
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++] == '-');
108
109 std::string digits;
110 long frac_digits = 0;
111 bool any = false;
112 while (i < n && text[i] >= '0' && text[i] <= '9') {
113 digits += text[i++];
114 any = true;
115 }
116 if (i < n && text[i] == '.') {
117 ++i;
118 while (i < n && text[i] >= '0' && text[i] <= '9') {
119 digits += text[i++];
120 ++frac_digits;
121 any = true;
122 }
123 }
124 if (!any) throw InputError("not a decimal number: '" + text + "'");
125
126 long exponent = 0;
127 if (i < n && (text[i] == 'e' || text[i] == 'E')) {
128 ++i;
129 bool eneg = false;
130 if (i < n && (text[i] == '+' || text[i] == '-')) eneg = (text[i++] == '-');
131 std::string edig;
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;
136 }
137 while (i < n && (text[i] == ' ' || text[i] == '\t')) ++i;
138 if (i != n) throw InputError("trailing characters in number '" + text + "'");
139
140 // leading-zero-as-octal fix: see _kb/14-cpp-multiprecision.md
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);
144 Rational r(mantissa);
145 const long scale = exponent - frac_digits;
146 if (scale > 0)
147 r *= Rational(pow10_bigint(static_cast<unsigned>(scale)));
148 else if (scale < 0)
149 r /= Rational(pow10_bigint(static_cast<unsigned>(-scale)));
150 return negative ? Rational(-r) : r;
151}
152
153/** double value of a decimal literal, including the "a/b" fraction form. */
154inline double double_from_decimal(const std::string& text) {
155 const std::size_t slash = text.find('/');
156 if (slash != std::string::npos)
157 return double_from_decimal(text.substr(0, slash)) /
158 double_from_decimal(text.substr(slash + 1));
159 const char* s = text.c_str();
160 char* end = nullptr;
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 + "'");
165 return v;
166}
167
168// ---------------------------------------------------------------------------
169// Decimal literal -> the algorithm's number type
170// ---------------------------------------------------------------------------
171
172template <class T>
174
175template <>
176struct NumFromDecimal<double> {
177 static double parse(const std::string& text) { return double_from_decimal(text); }
178};
179
180template <>
182 static Rational parse(const std::string& text) { return rational_from_decimal(text); }
183};
184
185template <unsigned D>
187 static Real<D> parse(const std::string& text) {
188 const std::size_t slash = text.find('/');
189 if (slash != std::string::npos)
190 return Real<D>(parse(text.substr(0, slash))) / Real<D>(parse(text.substr(slash + 1)));
191 // Reject what the backend would accept loosely, so a typo is an error
192 // rather than a silent zero, then let the backend do the rounding.
193 (void)double_from_decimal(text);
194 return Real<D>(text);
195 }
196};
197
198/** The decimal literal behind a JSON scalar, per the policy in the file header. */
199inline std::string decimal_text(const Json& j, const std::string& where) {
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>());
203 if (j.is_number_float()) return shortest_decimal(j.get<double>());
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()));
207}
208
209template <class T>
210T number_from_json(const Json& j, const std::string& where) {
211 return NumFromDecimal<T>::parse(decimal_text(j, where));
212}
213
214// ---------------------------------------------------------------------------
215// Shapes
216// ---------------------------------------------------------------------------
217
218/**
219 * A matrix from a JSON value: 2-D array as rows, 1-D array as a row vector,
220 * scalar as 1x1, empty array as the empty matrix. Ragged rows are an error.
221 */
222template <class T>
223Matrix<T> matrix_from_json(const Json& j, const std::string& where) {
224 if (!j.is_array()) {
225 Matrix<T> m(1, 1);
226 m(0, 0) = number_from_json<T>(j, where);
227 return m;
228 }
229 if (j.empty()) return Matrix<T>();
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));
240 }
241 if (cols == 0) return Matrix<T>();
242 Matrix<T> m(rows, cols);
243 for (std::size_t i = 0; i < rows; ++i)
244 for (std::size_t k = 0; k < cols; ++k)
245 m(i, k) = number_from_json<T>(j[i][k], where);
246 return m;
247 }
248 Matrix<T> m(1, j.size());
249 for (std::size_t k = 0; k < j.size(); ++k) m(0, k) = number_from_json<T>(j[k], where);
250 return m;
251}
252
253/** A flat numeric vector: 1-D array, or a 1-row / 1-column 2-D array. */
254template <class T>
255std::vector<T> vector_from_json(const Json& j, const std::string& where) {
256 const Matrix<T> m = matrix_from_json<T>(j, where);
257 if (m.empty()) return std::vector<T>();
258 if (m.rows() != 1 && m.cols() != 1)
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];
263 return v;
264}
265
266/** An integer vector; a non-integral entry is an error, never a truncation. */
267inline std::vector<int> int_vector_from_json(const Json& j, const std::string& where) {
268 std::vector<Json> flat;
269 if (!j.is_array()) {
270 flat.push_back(j);
271 } else {
272 for (const Json& e : j) {
273 if (e.is_array())
274 for (const Json& f : e) flat.push_back(f);
275 else
276 flat.push_back(e);
277 }
278 }
279 std::vector<int> v;
280 v.reserve(flat.size());
281 for (const Json& e : flat) {
282 const std::string text = decimal_text(e, where);
283 const Rational r = rational_from_decimal(text);
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));
290 }
291 return v;
292}
293
294// ---------------------------------------------------------------------------
295// Encoding results
296// ---------------------------------------------------------------------------
297
298template <class T>
300
301template <>
302struct EncodeScalar<double> {
303 static Json encode(const double& v) { return Json(v); }
304};
305
306template <>
308 static Json encode(const Rational& v) {
309 Json j;
310 j["double"] = num_traits<Rational>::to_double(v);
313 return j;
314 }
315};
316
317template <unsigned D>
318struct EncodeScalar<Real<D>> {
319 static Json encode(const Real<D>& v) {
320 Json j;
321 j["double"] = num_traits<Real<D>>::to_double(v);
322 j["dec"] = v.str(static_cast<std::streamsize>(D), std::ios_base::scientific);
323 return j;
324 }
325};
326
327template <class T>
328Json encode_scalar(const T& v) {
329 return EncodeScalar<T>::encode(v);
330}
331
332template <class T>
333Json encode_vector(const std::vector<T>& v) {
334 Json a = Json::array();
335 for (const T& x : v) a.push_back(encode_scalar(x));
336 return a;
337}
338
339template <class T>
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)));
345 a.push_back(row);
346 }
347 return a;
348}
349
350inline Json encode_ints(const std::vector<int>& v) {
351 Json a = Json::array();
352 for (int x : v) a.push_back(x);
353 return a;
354}
355
356/** A list of matrices, the shape the MMAP/BMAP families return as {D0,D1,...}. */
357template <class T>
358Json encode_matrices(const std::vector<Matrix<T> >& v) {
359 Json a = Json::array();
360 for (const Matrix<T>& m : v) a.push_back(encode_matrix(m));
361 return a;
362}
363
364/** A list of vectors, the shape a per-class or per-segment result returns. */
365template <class T>
366Json encode_vectors(const std::vector<std::vector<T> >& v) {
367 Json a = Json::array();
368 for (const std::vector<T>& x : v) a.push_back(encode_vector(x));
369 return a;
370}
371
372/**
373 * A count. Written as a plain JSON integer at every arithmetic: an iteration
374 * count or a dimension is exact in all of them, so wrapping it in the
375 * {"double",...} envelope the scalars use would suggest a precision question
376 * that a cardinal number does not have.
377 */
378inline Json encode_count(std::size_t n) { return Json(static_cast<std::uint64_t>(n)); }
379
380// ---------------------------------------------------------------------------
381// Argument object
382// ---------------------------------------------------------------------------
383
384/**
385 * Named-argument reader over the parsed JSON object. Every read marks the key;
386 * done() then refuses any key the function did not ask for, naming it and
387 * listing what the function does accept. Silently ignoring an unknown key is
388 * how a caller ends up with the default value of the argument they thought
389 * they were setting.
390 */
391class Args {
392public:
393 Args(const Json& j, std::string function) : j_(j), fn_(std::move(function)) {
394 if (!j_.is_object())
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()));
398 }
399
400 bool has(const char* key) const { return j_.find(key) != j_.end(); }
401
402 /** Required argument; throws when absent. */
403 const Json& get(const char* key) {
404 seen_.insert(key);
405 auto it = j_.find(key);
406 if (it == j_.end()) throw InputError(fn_ + ": missing required argument '" + key + "'");
407 return *it;
408 }
409
410 /** Optional argument; returns nullptr when absent. */
411 const Json* opt(const char* key) {
412 seen_.insert(key);
413 auto it = j_.find(key);
414 return it == j_.end() ? nullptr : &*it;
415 }
416
417 template <class T>
418 Matrix<T> matrix(const char* key) {
419 return matrix_from_json<T>(get(key), fn_ + ": " + key);
420 }
421
422 template <class T>
423 Matrix<T> matrix_or_empty(const char* key) {
424 const Json* v = opt(key);
425 return v ? matrix_from_json<T>(*v, fn_ + ": " + key) : Matrix<T>();
426 }
427
428 template <class T>
429 std::vector<T> vector_or_empty(const char* key) {
430 const Json* v = opt(key);
431 return v ? vector_from_json<T>(*v, fn_ + ": " + key) : std::vector<T>();
432 }
433
434 /** Required vector argument; absent is an error, not an empty vector. */
435 template <class T>
436 std::vector<T> vector(const char* key) {
437 return vector_from_json<T>(get(key), fn_ + ": " + key);
438 }
439
440 /**
441 * Required list of matrices, e.g. the per-class D1c of an MMAP. A single
442 * matrix is NOT silently promoted to a one-element list: the two shapes
443 * mean different models and the caller must say which one it meant.
444 */
445 template <class T>
446 std::vector<Matrix<T> > matrices(const char* key) {
447 const Json& j = get(key);
448 const std::string where = fn_ + ": " + key;
449 if (!j.is_array())
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)
454 out.push_back(matrix_from_json<T>(j[i], where + "[" + std::to_string(i) + "]"));
455 return out;
456 }
457
458 template <class T>
459 std::vector<Matrix<T> > matrices_or_empty(const char* key) {
460 seen_.insert(key);
461 if (!has(key)) return std::vector<Matrix<T> >();
462 return matrices<T>(key);
463 }
464
465 /** Required numeric argument, at the arithmetic of the call. */
466 template <class T>
467 T number(const char* key) {
468 return number_from_json<T>(get(key), fn_ + ": " + key);
469 }
470
471 /**
472 * A required nonnegative count. Refuses a negative value naming the
473 * argument rather than wrapping it around into a huge unsigned.
474 */
475 std::size_t count(const char* key) {
476 const int v = required_integer(key);
477 if (v < 0)
478 throw InputError(fn_ + ": '" + key + "' is a count and cannot be negative, got " +
479 std::to_string(v));
480 return static_cast<std::size_t>(v);
481 }
482
483 std::size_t count(const char* key, std::size_t fallback) {
484 if (!has(key)) {
485 seen_.insert(key);
486 return fallback;
487 }
488 return count(key);
489 }
490
491 /** The same as count(), for the arguments the port declares `unsigned`. */
492 unsigned uinteger(const char* key) { return static_cast<unsigned>(count(key)); }
493
494 unsigned uinteger(const char* key, unsigned fallback) {
495 return static_cast<unsigned>(count(key, static_cast<std::size_t>(fallback)));
496 }
497
498 int required_integer(const char* key) {
499 const std::vector<int> got = int_vector_from_json(get(key), fn_ + ": " + key);
500 if (got.size() != 1) throw InputError(fn_ + ": '" + key + "' must be a single integer");
501 return got[0];
502 }
503
504 /** A flag. Accepts a JSON boolean or the integers 0 and 1, nothing else. */
505 bool boolean(const char* key, bool fallback) {
506 const Json* v = opt(key);
507 if (!v) return fallback;
508 if (v->is_boolean()) return v->get<bool>();
509 const std::vector<int> got = int_vector_from_json(*v, fn_ + ": " + key);
510 if (got.size() != 1 || (got[0] != 0 && got[0] != 1))
511 throw InputError(fn_ + ": '" + key + "' must be true, false, 0 or 1");
512 return got[0] != 0;
513 }
514
515 /** A string-valued option, e.g. a method or convention name. */
516 std::string text(const char* key, const std::string& fallback) {
517 const Json* v = opt(key);
518 if (!v) return fallback;
519 if (!v->is_string())
520 throw InputError(fn_ + ": '" + key + "' must be a string, got " +
521 std::string(v->type_name()));
522 return v->get<std::string>();
523 }
524
525 std::vector<int> ints(const char* key) {
526 return int_vector_from_json(get(key), fn_ + ": " + key);
527 }
528
529 std::vector<int> ints_or_empty(const char* key) {
530 const Json* v = opt(key);
531 return v ? int_vector_from_json(*v, fn_ + ": " + key) : std::vector<int>();
532 }
533
534 int integer(const char* key, int fallback) {
535 const Json* v = opt(key);
536 if (!v) return fallback;
537 const std::vector<int> got = int_vector_from_json(*v, fn_ + ": " + key);
538 if (got.size() != 1)
539 throw InputError(fn_ + ": '" + key + "' must be a single integer");
540 return got[0];
541 }
542
543 template <class T>
544 T scalar(const char* key, const T& fallback) {
545 const Json* v = opt(key);
546 return v ? number_from_json<T>(*v, fn_ + ": " + key) : fallback;
547 }
548
549 /**
550 * Refuse an argument the JSON boundary cannot carry, naming it, rather than
551 * proceeding as if it had not been given.
552 */
553 void unsupported(const char* key, const std::string& why) {
554 seen_.insert(key);
555 if (j_.find(key) != j_.end())
556 throw UnsupportedError(fn_ + ": argument '" + key + "' cannot cross the JSON API "
557 "boundary: " +
558 why);
559 }
560
561 void done() const {
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 += ", ";
567 accepted += k;
568 }
569 throw InputError(fn_ + ": unknown argument '" + it.key() + "'; " + fn_ +
570 " accepts: " + accepted);
571 }
572 }
573
574private:
575 const Json& j_;
576 std::string fn_;
577 std::set<std::string> seen_;
578};
579
580} // namespace reg
581} // namespace line
582
583#endif // LINE_REG_API_JSON_H
Malformed or inconsistent input (dimensions, negative populations, ...).
Definition error.h:37
InputError(const std::string &what)
Definition error.h:39
std::size_t size() const
Definition matrix.h:91
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
bool empty() const
Definition matrix.h:92
UnsupportedError(const std::string &what)
Definition error.h:51
unsigned uinteger(const char *key)
The same as count(), for the arguments the port declares unsigned.
Definition api_json.h:492
std::size_t count(const char *key)
A required nonnegative count.
Definition api_json.h:475
Matrix< T > matrix(const char *key)
Definition api_json.h:418
int integer(const char *key, int fallback)
Definition api_json.h:534
std::string text(const char *key, const std::string &fallback)
A string-valued option, e.g.
Definition api_json.h:516
const Json * opt(const char *key)
Optional argument; returns nullptr when absent.
Definition api_json.h:411
std::vector< T > vector_or_empty(const char *key)
Definition api_json.h:429
unsigned uinteger(const char *key, unsigned fallback)
Definition api_json.h:494
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...
Definition api_json.h:553
std::vector< Matrix< T > > matrices(const char *key)
Required list of matrices, e.g.
Definition api_json.h:446
std::vector< int > ints(const char *key)
Definition api_json.h:525
const Json & get(const char *key)
Required argument; throws when absent.
Definition api_json.h:403
bool has(const char *key) const
Definition api_json.h:400
int required_integer(const char *key)
Definition api_json.h:498
Matrix< T > matrix_or_empty(const char *key)
Definition api_json.h:423
T scalar(const char *key, const T &fallback)
Definition api_json.h:544
std::size_t count(const char *key, std::size_t fallback)
Definition api_json.h:483
std::vector< int > ints_or_empty(const char *key)
Definition api_json.h:529
std::vector< Matrix< T > > matrices_or_empty(const char *key)
Definition api_json.h:459
std::vector< T > vector(const char *key)
Required vector argument; absent is an error, not an empty vector.
Definition api_json.h:436
Args(const Json &j, std::string function)
Definition api_json.h:393
T number(const char *key)
Required numeric argument, at the arithmetic of the call.
Definition api_json.h:467
void done() const
Definition api_json.h:561
bool boolean(const char *key, bool fallback)
A flag.
Definition api_json.h:505
The exception types the port throws.
Dense matrix and non-owning view.
nlohmann::json Json
Definition api_json.h:63
Json encode_vectors(const std::vector< std::vector< T > > &v)
A list of vectors, the shape a per-class or per-segment result returns.
Definition api_json.h:366
double double_from_decimal(const std::string &text)
double value of a decimal literal, including the "a/b" fraction form.
Definition api_json.h:154
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,...
Definition api_json.h:223
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.
Definition api_json.h:267
Json encode_matrices(const std::vector< Matrix< T > > &v)
A list of matrices, the shape the MMAP/BMAP families return as {D0,D1,...}.
Definition api_json.h:358
Rational rational_from_decimal(const std::string &text)
Exact value of a decimal literal, with no rounding anywhere: sign, digits, optional fraction and opti...
Definition api_json.h:97
BigInt pow10_bigint(unsigned e)
Definition api_json.h:86
T number_from_json(const Json &j, const std::string &where)
Definition api_json.h:210
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.
Definition api_json.h:255
Json encode_vector(const std::vector< T > &v)
Definition api_json.h:333
Json encode_matrix(const Matrix< T > &m)
Definition api_json.h:340
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.
Definition api_json.h:199
std::string shortest_decimal(double v)
The shortest decimal literal that round-trips to v.
Definition api_json.h:76
Json encode_scalar(const T &v)
Definition api_json.h:328
Json encode_count(std::size_t n)
A count.
Definition api_json.h:378
Json encode_ints(const std::vector< int > &v)
Definition api_json.h:350
boost::multiprecision::cpp_int BigInt
Definition number.h:78
boost::multiprecision::number< boost::multiprecision::cpp_rational_backend, boost::multiprecision::et_off > Rational
Definition number.h:76
boost::multiprecision::number< boost::multiprecision::cpp_bin_float< Digits10 > > Real
Definition number.h:80
Number-type abstraction for the templated API port.
static Json encode(const Rational &v)
Definition api_json.h:308
static Json encode(const Real< D > &v)
Definition api_json.h:319
static Json encode(const double &v)
Definition api_json.h:303
static Rational parse(const std::string &text)
Definition api_json.h:182
static Real< D > parse(const std::string &text)
Definition api_json.h:187
static double parse(const std::string &text)
Definition api_json.h:177