9PropertyStore::PropertyStore(PropertyStore&& other) noexcept
10 : properties_(std::move(other.properties_)) {
14PropertyStore& PropertyStore::operator=(
const PropertyStore& other) {
16 auto candidate = other.properties_;
17 properties_.swap(candidate);
23PropertyStore& PropertyStore::operator=(PropertyStore&& other)
noexcept {
25 properties_ = std::move(other.properties_);
53 std::initializer_list<Sign> existing;
55 const Conflict conflicts[] = {
63 for (
const auto& conflict : conflicts) {
64 if (conflict.incoming !=
a) {
67 return std::find(conflict.existing.begin(), conflict.existing.end(),
b)
68 != conflict.existing.end();
75int PropertyStore::domain_specificity(Domain
domain)
const {
76 for (
int i = 0; i < 7; ++i) {
77 if (domain_order[i] ==
domain)
return i;
85std::unordered_set<Sign, SignHash> PropertyStore::get_implied_signs(
Sign sign)
const {
86 std::unordered_set<Sign, SignHash> implied;
108void PropertyStore::check_sign_contradiction(
const std::string& symbol,
109 const std::unordered_set<Sign, SignHash>& existing,
111 for (
Sign existing_sign : existing) {
113 throw std::invalid_argument(
114 "Contradiction for symbol '" + symbol +
"': cannot declare sign " +
115 sign_str(new_sign) +
" because it conflicts with existing sign " +
121void PropertyStore::check_domain_sign_consistency(
const std::string& symbol,
122 const SymbolProperties& props,
127 throw std::invalid_argument(
128 "Contradiction for symbol '" + symbol +
130 " is incompatible with existing sign Negative");
137 throw std::invalid_argument(
138 "Contradiction for symbol '" + symbol +
139 "': domain PositiveInt is incompatible with existing sign Zero");
142 throw std::invalid_argument(
143 "Contradiction for symbol '" + symbol +
144 "': domain PositiveInt is incompatible with existing sign NonPositive");
151void PropertyStore::declare_domain_unchecked(
const std::string& symbol,
Domain domain) {
152 auto& props = properties_[symbol];
155 if (props.most_specific_domain ==
domain) {
161 if (domain_specificity(
domain) < domain_specificity(props.most_specific_domain)) {
166 if (props.transcendental && domain_specificity(
domain) > domain_specificity(
Domain::Real)) {
167 throw std::invalid_argument(
168 "Contradiction for symbol '" + symbol +
170 " because symbol is Transcendental (existing domain: " +
171 domain_str(props.most_specific_domain) +
")");
175 check_domain_sign_consistency(symbol, props,
domain);
178 props.most_specific_domain =
domain;
183 return declare_domain_checked(symbol,
domain);
187 const std::string& symbol,
190 [&](PropertyStore& candidate) {
191 candidate.declare_domain_unchecked(symbol,
domain);
197void PropertyStore::declare_sign_unchecked(
const std::string& symbol,
Sign sign) {
198 auto& props = properties_[symbol];
201 if (props.signs.count(sign)) {
206 std::unordered_set<Sign, SignHash> to_add;
208 auto implied = get_implied_signs(sign);
209 to_add.insert(implied.begin(), implied.end());
212 for (
Sign s : to_add) {
213 if (!props.signs.count(s)) {
214 check_sign_contradiction(symbol, props.signs, s);
221 for (
Sign s : to_add) {
223 throw std::invalid_argument(
224 "Contradiction for symbol '" + symbol +
225 "': cannot declare sign Negative because it conflicts with domain " +
231 for (
Sign s : to_add) {
233 throw std::invalid_argument(
234 "Contradiction for symbol '" + symbol +
235 "': cannot declare sign Zero because it conflicts with domain PositiveInt");
238 throw std::invalid_argument(
239 "Contradiction for symbol '" + symbol +
240 "': cannot declare sign NonPositive because it conflicts with domain PositiveInt");
246 props.signs.insert(to_add.begin(), to_add.end());
250 if (domain_specificity(
Domain::Integer) > domain_specificity(props.most_specific_domain)) {
257 const std::string& symbol,
Sign sign) {
258 return declare_sign_checked(symbol, sign);
262 const std::string& symbol,
265 [&](PropertyStore& candidate) {
266 candidate.declare_sign_unchecked(symbol, sign);
272void PropertyStore::declare_parity_unchecked(
const std::string& symbol,
Parity parity) {
273 auto& props = properties_[symbol];
276 if (props.parity == parity) {
282 props.parity = parity;
288 throw std::invalid_argument(
289 "Contradiction for symbol '" + symbol +
"': parity conflict (Even vs Odd)");
293 if (domain_specificity(
Domain::Integer) > domain_specificity(props.most_specific_domain)) {
297 props.parity = parity;
301 const std::string& symbol,
Parity parity) {
302 return declare_parity_checked(symbol, parity);
306 const std::string& symbol,
309 [&](PropertyStore& candidate) {
310 candidate.declare_parity_unchecked(symbol, parity);
316void PropertyStore::declare_bounded_unchecked(
const std::string& symbol,
318 std::optional<Interval> bounds) {
319 auto& props = properties_[symbol];
321 if (props.boundedness == bounded) {
323 props.bounds = std::move(bounds);
329 props.boundedness = bounded;
330 props.bounds = std::nullopt;
335 throw std::invalid_argument(
336 "Contradiction for symbol '" + symbol +
337 "': boundedness conflict (Bounded vs Unbounded)");
340 props.boundedness = bounded;
341 props.bounds = std::move(bounds);
349 const Interval& bounds,
const std::optional<Interval>& existing,
351 constexpr const char* operation =
"declare_bounded";
352 auto incoming = IntervalUnion::from_intervals_checked(
360 if (incoming.value().is_empty()) {
363 "bounded interval must not be empty", operation);
366 std::optional<Interval> candidate =
367 incoming.value().intervals().front();
372 auto current = IntervalUnion::from_intervals_checked(
377 auto intersection = current.value().intersect_checked(
382 if (intersection.value().is_empty()) {
385 "bounded interval contradicts existing bounds", operation);
387 candidate = intersection.value().intervals().front();
395 const std::string& symbol,
397 std::optional<Interval> bounds) {
398 return declare_bounded_checked(symbol, bounded, std::move(bounds));
402 const std::string& symbol,
404 std::optional<Interval> bounds) {
406 return declare_bounded_checked(
407 symbol, bounded, std::move(bounds),
context);
411 const std::string& symbol,
413 std::optional<Interval> bounds,
415 constexpr const char*
operation =
"declare_bounded";
416 if (symbol.empty()) {
422 "interval bounds require Bounded classification", operation);
426 std::optional<Interval> committed_bounds;
429 *bounds, get_bounds(symbol),
context);
433 committed_bounds = std::move(candidate.value());
436 PropertyStore candidate = *
this;
437 candidate.declare_bounded_unchecked(
438 symbol, bounded, std::move(committed_bounds));
439 *
this = std::move(candidate);
441 }
catch (
const std::bad_alloc&) {
444 "property-store allocation failed", operation);
445 }
catch (
const std::invalid_argument& error) {
448 }
catch (
const std::exception& error) {
456Domain PropertyStore::get_domain(
const std::string& symbol)
const {
457 auto it = properties_.find(symbol);
458 if (it == properties_.end()) {
461 return it->second.most_specific_domain;
464std::unordered_set<Sign, SignHash> PropertyStore::get_signs(
const std::string& symbol)
const {
465 auto it = properties_.find(symbol);
466 if (it == properties_.end()) {
469 return it->second.signs;
472Parity PropertyStore::get_parity(
const std::string& symbol)
const {
473 auto it = properties_.find(symbol);
474 if (it == properties_.end()) {
477 return it->second.parity;
480Boundedness PropertyStore::get_boundedness(
const std::string& symbol)
const {
481 auto it = properties_.find(symbol);
482 if (it == properties_.end()) {
485 return it->second.boundedness;
488std::optional<Interval> PropertyStore::get_bounds(
const std::string& symbol)
const {
489 auto it = properties_.find(symbol);
490 if (it == properties_.end()) {
493 return it->second.bounds;
496bool PropertyStore::has_sign(
const std::string& symbol,
Sign sign)
const {
497 auto it = properties_.find(symbol);
498 if (it == properties_.end()) {
501 return it->second.signs.count(sign) > 0;
504bool PropertyStore::has_domain(
const std::string& symbol,
Domain domain)
const {
505 auto it = properties_.find(symbol);
506 if (it == properties_.end()) {
512 return domain_specificity(it->second.most_specific_domain) >= domain_specificity(
domain);
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(std::optional< Interval > value)
std::string sign_str(Sign s)
bool signs_conflict(Sign a, Sign b)
BoundsCandidateResult bounded_interval_candidate(const Interval &bounds, const std::optional< Interval > &existing, ComputationContext &context)
constexpr Domain domain_order[]
Result< std::optional< Interval > > BoundsCandidateResult
ComputationContext const std::string & operation
PropertyStoreResult checked_property_update(PropertyStore &store, const std::string &symbol, const std::string &operation, F &&update)
std::string domain_str(Domain domain)
PropertyStoreResult invalid_empty_symbol(const std::string &operation)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
Result< void > PropertyStoreResult
Domain
Mathematical domain hierarchy (least specific → most specific). Complex ⊃ Real ⊃ Algebraic ⊃ Rational...
@ Complex
Complex numbers (default, least specific)
@ Rational
Rational numbers
@ Natural
Non-negative integers (0, 1, 2, ...)
@ Algebraic
Algebraic numbers (roots of polynomials with rational coefficients)
@ PositiveInt
Positive integers (1, 2, 3, ...)
Boundedness
Boundedness classification for symbols.
@ Unknown
Boundedness not determined
@ Bounded
Symbol has finite bounds
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::shared_ptr< SymbolicExpr > & a
const NumberDomainSet & domain
Parity
Integer parity classification.
@ Unknown
Parity not determined
Sign
Sign classification for symbols and expressions.