LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
interval_serialization.cpp
浏览该文件的文档.
1#include "interval.hpp"
4#include <cctype>
5#include <cmath>
6#include <stdexcept>
7
8namespace LMCAS {
9
10std::string IntervalUnion::to_string() const {
11
12 if (intervals_.empty()) {
13 return "\xe2\x88\x85";
14 }
15
16 if (intervals_.size() == 1 && intervals_[0].is_entire_line()) {
17 return "(-\xe2\x88\x9e, +\xe2\x88\x9e)";
18 }
19
20 std::string result;
21 for (size_t i = 0; i < intervals_.size(); ++i) {
22 if (i > 0) {
23 result += " \xe2\x88\xaa ";
24 }
25
26 const Interval& iv = intervals_[i];
27
28 if (iv.lower.is_open) {
29 result += "(";
30 } else {
31 result += "[";
32 }
33
34 if (iv.lower.is_neg_infinity) {
35 result += "-\xe2\x88\x9e";
36 } else if (iv.lower.value) {
37 result += iv.lower.value->to_string();
38 } else {
39 result += "0";
40 }
41
42 result += ", ";
43
44 if (iv.upper.is_pos_infinity) {
45 result += "+\xe2\x88\x9e";
46 } else if (iv.upper.value) {
47 result += iv.upper.value->to_string();
48 } else {
49 result += "0";
50 }
51
52 if (iv.upper.is_open) {
53 result += ")";
54 } else {
55 result += "]";
56 }
57 }
58
59 return result;
60}
61
62static bool starts_with_at(const std::string& str, size_t pos, const std::string& prefix) {
63 if (pos + prefix.size() > str.size()) {
64 return false;
65 }
66 return str.compare(pos, prefix.size(), prefix) == 0;
67}
68
69static size_t skip_ws(const std::string& str, size_t pos) {
70 while (pos < str.size() && std::isspace(static_cast<unsigned char>(str[pos]))) {
71 ++pos;
72 }
73 return pos;
74}
75
76static bool consume_digits(const std::string& str, size_t& pos) {
77 const size_t start = pos;
78 while (pos < str.size() &&
79 std::isdigit(static_cast<unsigned char>(str[pos]))) {
80 ++pos;
81 }
82 return pos != start;
83}
84
85static void consume_sign(const std::string& str, size_t& pos) {
86 if (pos < str.size() && (str[pos] == '-' || str[pos] == '+')) ++pos;
87}
88
89static std::shared_ptr<SymbolicExpr> parse_fraction(
90 const std::string& str, size_t start, size_t& pos) {
91 const size_t slash = pos++;
92 const size_t denominator_start = pos;
93 if (!consume_digits(str, pos)) {
94 pos = start;
95 return nullptr;
96 }
97 const BigInt numerator(str.substr(start, slash - start));
98 const BigInt denominator(str.substr(denominator_start, pos - denominator_start));
99 if (denominator.is_zero()) {
100 pos = start;
101 return nullptr;
102 }
103 return SymbolicExpr::number(Rational(numerator, denominator));
104}
105
106static std::shared_ptr<SymbolicExpr> numeric_token_value(
107 const std::string& str, size_t start, size_t& pos, bool decimal) {
108 const std::string token = str.substr(start, pos - start);
109 if (!decimal) {
110 return SymbolicExpr::number(BigInt(token));
111 }
112 return SymbolicExpr::number(Rational(token));
113}
114
115static std::shared_ptr<SymbolicExpr> parse_approximate_atom(
116 const std::string& str, size_t& pos) {
117 const size_t start = pos;
118 pos = skip_ws(str, pos + 7);
119 const auto token_start = pos;
120 while (pos < str.size() && str[pos] != ')' &&
121 !std::isspace(static_cast<unsigned char>(str[pos]))) { ++pos; }
122 const std::string_view token(str.data() + token_start, pos - token_start);
123 pos = skip_ws(str, pos);
124 if (pos == str.size() || str[pos] != ')') {
125 pos = start;
126 return nullptr;
127 }
128 ++pos; /**< 消耗原子的结束分隔符,保留区间的分隔符。 */
130 if (!value) {
131 pos = start;
132 return nullptr;
133 }
134 return SymbolicExpr::number(value.value());
135}
136
137static std::shared_ptr<SymbolicExpr> parse_numeric_value(
138 const std::string& str, size_t& pos) {
139 const size_t start = pos;
140 if (starts_with_at(str, pos, "approx(")) {
141 return parse_approximate_atom(str, pos);
142 }
143 consume_sign(str, pos);
144 bool has_digit = consume_digits(str, pos);
145 if (has_digit && pos < str.size() && str[pos] == '/') {
146 return parse_fraction(str, start, pos);
147 }
148 bool decimal = false;
149 if (pos < str.size() && str[pos] == '.') {
150 decimal = true;
151 ++pos;
152 has_digit = consume_digits(str, pos) || has_digit;
153 }
154 if (!has_digit) {
155 pos = start;
156 return nullptr;
157 }
158 if (pos < str.size() && (str[pos] == 'e' || str[pos] == 'E')) {
159 decimal = true;
160 ++pos;
161 consume_sign(str, pos);
162 if (!consume_digits(str, pos)) {
163 pos = start;
164 return nullptr;
165 }
166 }
167 return numeric_token_value(str, start, pos, decimal);
168}
169
170static bool parse_endpoint_value(const std::string& str, size_t& pos, Endpoint& ep) {
171 pos = skip_ws(str, pos);
172
173 if (starts_with_at(str, pos, "-\xe2\x88\x9e")) {
174 ep.is_neg_infinity = true;
175 ep.is_pos_infinity = false;
176 ep.is_open = true;
177 ep.value = nullptr;
178 pos += 4;
179 return true;
180 }
181
182 if (starts_with_at(str, pos, "+\xe2\x88\x9e")) {
183 ep.is_pos_infinity = true;
184 ep.is_neg_infinity = false;
185 ep.is_open = true;
186 ep.value = nullptr;
187 pos += 4;
188 return true;
189 }
190
191 auto val = parse_numeric_value(str, pos);
192 if (!val) {
193 return false;
194 }
195
196 ep.value = val;
197 ep.is_neg_infinity = false;
198 ep.is_pos_infinity = false;
199
200 return true;
201}
202
204 const std::string& str, size_t& pos, Endpoint& lower) {
205 pos = skip_ws(str, pos);
206 if (pos >= str.size()) {
207 return false;
208 }
209 const char bracket = str[pos];
210 if (bracket != '(' && bracket != '[') {
211 return false;
212 }
213 const bool open = bracket == '(';
214 ++pos;
215 if (!parse_endpoint_value(str, pos, lower)) {
216 return false;
217 }
218 if (lower.is_pos_infinity || (lower.is_neg_infinity && !open)) {
219 return false;
220 }
221 if (!lower.is_neg_infinity) lower.is_open = open;
222 return true;
223}
224
226 const std::string& str, size_t& pos, Endpoint& upper) {
227 if (!parse_endpoint_value(str, pos, upper)) {
228 return false;
229 }
230 pos = skip_ws(str, pos);
231 if (pos >= str.size()) {
232 return false;
233 }
234 const char bracket = str[pos];
235 if (bracket != ')' && bracket != ']') {
236 return false;
237 }
238 const bool open = bracket == ')';
239 ++pos;
240 if (upper.is_neg_infinity || (upper.is_pos_infinity && !open)) {
241 return false;
242 }
243 if (!upper.is_pos_infinity) upper.is_open = open;
244 return true;
245}
246
248 const std::string& str, size_t& pos, Interval& iv) {
249 Endpoint lower;
250 if (!parse_interval_lower(str, pos, lower)) {
251 return false;
252 }
253 pos = skip_ws(str, pos);
254 if (pos >= str.size() || str[pos] != ',') {
255 return false;
256 }
257 ++pos;
258 Endpoint upper;
259 if (!parse_interval_upper(str, pos, upper)) {
260 return false;
261 }
262 iv.lower = std::move(lower);
263 iv.upper = std::move(upper);
264 return true;
265}
266
267std::optional<IntervalUnion> IntervalUnion::parse(const std::string& str) {
268 if (str.empty()) {
269 return std::nullopt;
270 }
271
272 if (str == "\xe2\x88\x85") {
273 return IntervalUnion::empty();
274 }
275
276 try {
277 std::vector<Interval> intervals;
278 size_t pos = 0;
279
280 Interval iv;
281 if (!parse_single_interval(str, pos, iv)) {
282 return std::nullopt;
283 }
284 intervals.push_back(std::move(iv));
285
286 while (pos < str.size()) {
287 pos = skip_ws(str, pos);
288 if (pos >= str.size()) break;
289
290 if (!starts_with_at(str, pos, "\xe2\x88\xaa")) {
291 return std::nullopt;
292 }
293 pos += 3;
294 pos = skip_ws(str, pos);
295
296 Interval next_iv;
297 if (!parse_single_interval(str, pos, next_iv)) {
298 return std::nullopt;
299 }
300 intervals.push_back(std::move(next_iv));
301 }
302
303 auto normalized =
304 IntervalUnion::from_intervals_checked(std::move(intervals));
305 if (!normalized) {
306 return std::nullopt;
307 }
308 return std::move(normalized.value());
309 } catch (const std::invalid_argument&) {
310 return std::nullopt;
311 } catch (const std::out_of_range&) {
312 return std::nullopt;
313 }
314}
315
316static std::shared_ptr<const SymbolicNode> interval_to_expr(
317 const Interval& interval, const std::shared_ptr<const SymbolicNode>& variable) {
318 std::shared_ptr<const SymbolicNode> lower;
319 std::shared_ptr<const SymbolicNode> upper;
320 if (!interval.lower.is_neg_infinity) {
321 auto bound = interval.lower.value ? LMCAS::detail::node(interval.lower.value)
323 const auto op = interval.lower.is_open ? RelationalNode::Op::GT
324 : RelationalNode::Op::GEQ;
326 }
327 if (!interval.upper.is_pos_infinity) {
328 auto bound = interval.upper.value ? LMCAS::detail::node(interval.upper.value)
330 const auto op = interval.upper.is_open ? RelationalNode::Op::LT
331 : RelationalNode::Op::LEQ;
333 }
334 if (lower && upper) {
336 }
337 return lower ? lower : upper;
338}
339
340std::shared_ptr<SymbolicExpr> IntervalUnion::to_expr(const std::string& var) const {
341 if (is_empty()) {
344 std::vector<std::shared_ptr<const SymbolicNode>>{}));
345 }
346
347 auto var_node = LMCAS::detail::node(SymbolicExpr::variable(var));
348 if (is_entire_line()) {
349 auto zero = LMCAS::detail::node(SymbolicExpr::number(0));
352 zero, zero, RelationalNode::Op::EQ));
353 }
354
355 if (intervals_.size() == 1) {
356 auto node = interval_to_expr(intervals_[0], var_node);
357 if (!node) {
358 return nullptr;
359 }
361 }
362
363 auto result = interval_to_expr(intervals_[0], var_node);
364 for (size_t i = 1; i < intervals_.size(); ++i) {
365 auto next = interval_to_expr(intervals_[i], var_node);
366 if (result && next) {
368 } else if (next) {
369 result = next;
370 }
371
372 }
373
374 if (!result) {
375 return nullptr;
376 }
378}
379
380}
区间与区间并集 IntervalUnion,用于不等式解集表示。
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< double > parse_finite_decimal(std::string_view token)
std::shared_ptr< const Node > make_node(Args &&... args)
const std::string & variable
__attribute__((visibility("default"))) ExprResult function(const std __attribute__((visibility("default"))) ExprResult finite_set(std ExprPtr upper
static std::shared_ptr< SymbolicExpr > parse_numeric_value(const std::string &str, size_t &pos)
static bool parse_interval_lower(const std::string &str, size_t &pos, Endpoint &lower)
ExprResult interval(ExprPtr lower, ExprPtr upper, bool lower_closed, bool upper_closed)
const ExprPtr & lower
static bool parse_interval_upper(const std::string &str, size_t &pos, Endpoint &upper)
static size_t skip_ws(const std::string &str, size_t pos)
static std::shared_ptr< SymbolicExpr > parse_approximate_atom(const std::string &str, size_t &pos)
const ExprPtr RelationOp op
static std::shared_ptr< SymbolicExpr > numeric_token_value(const std::string &str, size_t start, size_t &pos, bool decimal)
static bool starts_with_at(const std::string &str, size_t pos, const std::string &prefix)
static bool consume_digits(const std::string &str, size_t &pos)
const std::string const ExprPtr & value
static void consume_sign(const std::string &str, size_t &pos)
static std::shared_ptr< const SymbolicNode > interval_to_expr(const Interval &interval, const std::shared_ptr< const SymbolicNode > &variable)
static std::shared_ptr< SymbolicExpr > parse_fraction(const std::string &str, size_t start, size_t &pos)
const ExprPtr & denominator
static bool parse_single_interval(const std::string &str, size_t &pos, Interval &iv)
static bool parse_endpoint_value(const std::string &str, size_t &pos, Endpoint &ep)
内部 AST 定义。