LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
quantity.cpp
浏览该文件的文档.
1#include "quantity.hpp"
2
3#include <exception>
4#include <utility>
5
7
8namespace LMCAS {
9namespace {
10
11constexpr const char* kOperation = "quantity";
12
14 std::shared_ptr<const SymbolicNode> value;
15 DimensionSignature dimension;
16 Rational scale = Rational(1);
17 std::string display;
18};
19
20QuantityResult failure(CasErrc code, std::string message) {
21 return QuantityResult::failure(code, std::move(message), kOperation);
22}
23
24QuantityView view(const std::shared_ptr<SymbolicExpr>& expression) {
25 if (auto quantity = std::dynamic_pointer_cast<const QuantityNode>(detail::node(expression))) {
26 return {quantity->value(), quantity->dimension(), quantity->scale_to_base(),
27 quantity->display_unit()};
28 }
29 return {detail::node(expression), DimensionSignature{}, Rational(1), {}};
30}
31
32std::shared_ptr<const SymbolicNode> rational_node(const Rational& value) {
34}
35
36std::shared_ptr<const SymbolicNode> scaled_value(const QuantityView& quantity) {
37 if (quantity.scale == Rational(1)) return quantity.value;
38 return SymbolicFactory::create_multiply({quantity.value, rational_node(quantity.scale)});
39}
40
41QuantityResult make_quantity(std::shared_ptr<const SymbolicNode> value,
42 DimensionSignature dimension,
43 Rational scale,
44 std::string display) {
45 try {
46 if (dimension.is_dimensionless()) {
48 scale == Rational(1)
49 ? std::move(value)
51 }
53 detail::make_node<QuantityNode>(std::move(value), std::move(dimension),
54 std::move(scale), std::move(display))));
55 } catch (const std::bad_alloc&) {
56 return failure(CasErrc::ResourceLimit, "quantity allocation failed");
57 } catch (const std::exception& error) {
58 return failure(CasErrc::InvalidArgument, error.what());
59 }
60}
61
62Result<Rational> exact_rational(const std::shared_ptr<const SymbolicNode>& node) {
63 auto number = std::dynamic_pointer_cast<const NumberNode>(node);
64 if (!number) return Result<Rational>::failure(
65 CasErrc::UnitInvalid, "quantity exponent must be an exact rational", kOperation);
66 if (std::holds_alternative<BigInt>(number->value())) {
67 return Result<Rational>::success(Rational(std::get<BigInt>(number->value())));
68 }
69 if (std::holds_alternative<Rational>(number->value())) {
70 return Result<Rational>::success(std::get<Rational>(number->value()));
71 }
73 CasErrc::UnitInvalid, "approximate quantity exponents are not supported", kOperation);
74}
75
76Result<void> checked_binary(const std::shared_ptr<SymbolicExpr>& lhs,
77 const std::shared_ptr<SymbolicExpr>& rhs,
79 const char* name) {
80 auto step = context.consume_steps(1, name);
81 if (!step) return step;
82 if (!lhs || !rhs || !detail::node(lhs) || !detail::node(rhs)) {
84 "quantity operands cannot be null", kOperation);
85 }
86 return Result<void>::success();
87}
88
89} // namespace
90
91QuantityResult attach_unit(const std::shared_ptr<SymbolicExpr>& value,
92 const std::string& unit,
94 auto step = context.consume_steps(1, kOperation);
95 if (!step) return QuantityResult::failure(step.error());
96 if (!value || !detail::node(value)) {
97 return failure(CasErrc::InvalidArgument, "quantity value cannot be null");
98 }
99 if (std::dynamic_pointer_cast<const QuantityNode>(detail::node(value))) {
100 return failure(CasErrc::UnitInvalid, "a quantity cannot receive a second unit");
101 }
102 auto definition = context.units().resolve(unit);
103 if (!definition) return QuantityResult::failure(definition.error());
104 return make_quantity(detail::node(value), definition.value().dimension,
105 definition.value().scale_to_base, unit);
106}
107
108QuantityResult attach_unit(const std::shared_ptr<SymbolicExpr>& value,
109 std::string display_unit,
112 auto step = context.consume_steps(1, kOperation);
113 if (!step) return QuantityResult::failure(step.error());
114 if (!value || !detail::node(value)) {
115 return failure(CasErrc::InvalidArgument, "quantity value cannot be null");
116 }
117 if (std::dynamic_pointer_cast<const QuantityNode>(detail::node(value))) {
118 return failure(CasErrc::UnitInvalid, "a quantity cannot receive a second unit");
119 }
120 if (display_unit.empty() || definition.scale_to_base <= Rational(0)) {
121 return failure(CasErrc::UnitInvalid, "resolved unit definition is invalid");
122 }
123 return make_quantity(detail::node(value), std::move(definition.dimension),
124 std::move(definition.scale_to_base),
125 std::move(display_unit));
126}
127
128QuantityResult convert_unit(const std::shared_ptr<SymbolicExpr>& expression,
129 const std::string& target_unit,
131 auto step = context.consume_steps(1, kOperation);
132 if (!step) return QuantityResult::failure(step.error());
133 auto quantity = expression
134 ? std::dynamic_pointer_cast<const QuantityNode>(detail::node(expression))
135 : nullptr;
136 if (!quantity) {
137 return failure(CasErrc::UnitStripTypeMismatch,
138 "unit conversion requires a quantity expression");
139 }
140 auto target = context.units().resolve(target_unit);
141 if (!target) return QuantityResult::failure(target.error());
142 if (quantity->dimension() != target.value().dimension) {
143 return failure(CasErrc::DimensionMismatch,
144 "unit conversion requires equal dimensions");
145 }
146 const Rational factor = quantity->scale_to_base() / target.value().scale_to_base;
148 {quantity->value(), rational_node(factor)});
149 return make_quantity(std::move(value), target.value().dimension,
150 target.value().scale_to_base, target_unit);
151}
152
153QuantityResult convert_unit(const std::shared_ptr<SymbolicExpr>& expression,
154 std::string display_unit,
157 auto step = context.consume_steps(1, kOperation);
158 if (!step) return QuantityResult::failure(step.error());
159 auto quantity = expression
160 ? std::dynamic_pointer_cast<const QuantityNode>(detail::node(expression))
161 : nullptr;
162 if (!quantity) {
163 return failure(CasErrc::UnitStripTypeMismatch,
164 "unit conversion requires a quantity expression");
165 }
166 if (display_unit.empty() || definition.scale_to_base <= Rational(0)) {
167 return failure(CasErrc::UnitInvalid, "resolved unit definition is invalid");
168 }
169 if (quantity->dimension() != definition.dimension) {
170 return failure(CasErrc::DimensionMismatch,
171 "unit conversion requires equal dimensions");
172 }
173 const Rational factor = quantity->scale_to_base() / definition.scale_to_base;
175 {quantity->value(), rational_node(factor)});
176 return make_quantity(std::move(value), std::move(definition.dimension),
177 std::move(definition.scale_to_base),
178 std::move(display_unit));
179}
180
181QuantityResult strip_unit(const std::shared_ptr<SymbolicExpr>& expression,
182 UnitStripMode mode,
184 auto step = context.consume_steps(1, kOperation);
185 if (!step) return QuantityResult::failure(step.error());
186 if (!expression || !detail::node(expression)) {
187 return failure(CasErrc::InvalidArgument,
188 "unit stripping requires an expression");
189 }
190 auto quantity = std::dynamic_pointer_cast<const QuantityNode>(
191 detail::node(expression));
192 if (!quantity) return QuantityResult::success(expression);
193 auto value = mode == UnitStripMode::BaseValue
195 {quantity->value(), rational_node(quantity->scale_to_base())})
196 : quantity->value();
198}
199
200Result<DimensionSignature> dimension_of(const SymbolicExpr& expression) {
201 if (!detail::node(expression)) {
203 CasErrc::InvalidArgument, "expression cannot be empty", kOperation);
204 }
205 if (auto quantity = std::dynamic_pointer_cast<const QuantityNode>(detail::node(expression))) {
206 return Result<DimensionSignature>::success(quantity->dimension());
207 }
208 return Result<DimensionSignature>::success(DimensionSignature{});
209}
210
211QuantityResult quantity_add(const std::shared_ptr<SymbolicExpr>& lhs,
212 const std::shared_ptr<SymbolicExpr>& rhs,
214 auto checked = checked_binary(lhs, rhs, context, "quantity.add");
215 if (!checked) return QuantityResult::failure(checked.error());
216 const auto left = view(lhs);
217 const auto right = view(rhs);
218 if (left.dimension != right.dimension) {
219 return failure(CasErrc::DimensionMismatch,
220 "addition requires operands with equal dimensions");
221 }
222 const Rational factor = right.scale / left.scale;
223 auto right_value = SymbolicFactory::create_multiply(
224 {right.value, rational_node(factor)});
225 return make_quantity(SymbolicFactory::create_add({left.value, right_value}),
226 left.dimension, left.scale, left.display);
227}
228
229QuantityResult quantity_subtract(const std::shared_ptr<SymbolicExpr>& lhs,
230 const std::shared_ptr<SymbolicExpr>& rhs,
232 auto checked = checked_binary(lhs, rhs, context, "quantity.subtract");
233 if (!checked) return QuantityResult::failure(checked.error());
234 const auto left = view(lhs);
235 const auto right = view(rhs);
236 if (left.dimension != right.dimension) {
237 return failure(CasErrc::DimensionMismatch,
238 "subtraction requires operands with equal dimensions");
239 }
240 const Rational factor = Rational(-1) * right.scale / left.scale;
241 auto right_value = SymbolicFactory::create_multiply(
242 {right.value, rational_node(factor)});
243 return make_quantity(SymbolicFactory::create_add({left.value, right_value}),
244 left.dimension, left.scale, left.display);
245}
246
247QuantityResult quantity_multiply(const std::shared_ptr<SymbolicExpr>& lhs,
248 const std::shared_ptr<SymbolicExpr>& rhs,
250 auto checked = checked_binary(lhs, rhs, context, "quantity.multiply");
251 if (!checked) return QuantityResult::failure(checked.error());
252 const auto left = view(lhs);
253 const auto right = view(rhs);
254 const auto dimension = left.dimension.multiplied_by(right.dimension);
255 const std::string display = left.display.empty() ? right.display
256 : right.display.empty() ? left.display : left.display + "*" + right.display;
257 return make_quantity(SymbolicFactory::create_multiply({left.value, right.value}),
258 dimension, left.scale * right.scale, display);
259}
260
261QuantityResult quantity_divide(const std::shared_ptr<SymbolicExpr>& lhs,
262 const std::shared_ptr<SymbolicExpr>& rhs,
264 auto checked = checked_binary(lhs, rhs, context, "quantity.divide");
265 if (!checked) return QuantityResult::failure(checked.error());
266 const auto left = view(lhs);
267 const auto right = view(rhs);
268 auto inverse = SymbolicFactory::create_power(right.value,
270 const std::string display = right.display.empty() ? left.display
271 : left.display.empty() ? "1/" + right.display : left.display + "/" + right.display;
272 return make_quantity(SymbolicFactory::create_multiply({left.value, inverse}),
273 left.dimension.divided_by(right.dimension),
274 left.scale / right.scale, display);
275}
276
277QuantityResult quantity_power(const std::shared_ptr<SymbolicExpr>& base,
278 const std::shared_ptr<SymbolicExpr>& exponent,
280 auto checked = checked_binary(base, exponent, context, "quantity.power");
281 if (!checked) return QuantityResult::failure(checked.error());
282 const auto exponent_view = view(exponent);
283 if (!exponent_view.dimension.is_dimensionless()) {
284 return failure(CasErrc::DimensionMismatch,
285 "a quantity exponent must be dimensionless");
286 }
287 const auto base_view = view(base);
288 auto base_value = scaled_value(base_view);
289 if (base_view.dimension.is_dimensionless()) {
290 return make_quantity(
291 SymbolicFactory::create_power(base_value, exponent_view.value),
292 DimensionSignature{}, Rational(1), {});
293 }
294 auto power = exact_rational(exponent_view.value);
295 if (!power) return QuantityResult::failure(power.error());
296 auto dimension = base_view.dimension.raised_to(power.value());
297 return make_quantity(
298 SymbolicFactory::create_power(base_value, exponent_view.value),
299 dimension, Rational(1), dimension.to_string());
300}
301
302} // namespace LMCAS
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(std::shared_ptr< SymbolicExpr > value)
static std::shared_ptr< const SymbolicNode > create_add(std::vector< std::shared_ptr< const SymbolicNode > > ops, detail::RewriteBudget *budget=nullptr)
创建加法节点,自动扁平化嵌套加法并消除零项.
static std::shared_ptr< const SymbolicNode > create_power(std::shared_ptr< const SymbolicNode > base, std::shared_ptr< const SymbolicNode > exponent)
创建幂运算节点,自动处理指数为 0/1 及底数为 0/1 的情况.
static std::shared_ptr< const SymbolicNode > create_multiply(std::vector< std::shared_ptr< const SymbolicNode > > ops, detail::RewriteBudget *budget=nullptr)
创建乘法节点,自动扁平化嵌套乘法并消除单位元.
static std::shared_ptr< const SymbolicNode > create_number(const ::LMCAS::BigInt &v)
Result< void > checked_binary(const std::shared_ptr< SymbolicExpr > &lhs, const std::shared_ptr< SymbolicExpr > &rhs, ComputationContext &context, const char *name)
std::shared_ptr< const SymbolicNode > rational_node(const Rational &value)
QuantityResult make_quantity(std::shared_ptr< const SymbolicNode > value, DimensionSignature dimension, Rational scale, std::string display)
std::shared_ptr< const SymbolicNode > scaled_value(const QuantityView &quantity)
QuantityView view(const std::shared_ptr< SymbolicExpr > &expression)
QuantityResult failure(CasErrc code, std::string message)
Result< Rational > exact_rational(const std::shared_ptr< const SymbolicNode > &node)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
std::shared_ptr< const Node > make_node(Args &&... args)
std::string display_unit
Result< DimensionSignature > dimension_of(const SymbolicExpr &expression)
Result< std::shared_ptr< SymbolicExpr > > QuantityResult
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
QuantityResult attach_unit(const std::shared_ptr< SymbolicExpr > &value, const std::string &unit, ComputationContext &context)
QuantityResult quantity_divide(const std::shared_ptr< SymbolicExpr > &lhs, const std::shared_ptr< SymbolicExpr > &rhs, ComputationContext &context)
std::string UnitDefinition definition
const ExprPtr & exponent
const ExprPtr & target
QuantityResult quantity_power(const std::shared_ptr< SymbolicExpr > &base, const std::shared_ptr< SymbolicExpr > &exponent, ComputationContext &context)
const std::string & unit
const ExprPtr & right
const std::string const ExprPtr & value
const ExprPtr & rhs
QuantityResult quantity_multiply(const std::shared_ptr< SymbolicExpr > &lhs, const std::shared_ptr< SymbolicExpr > &rhs, ComputationContext &context)
QuantityResult quantity_subtract(const std::shared_ptr< SymbolicExpr > &lhs, const std::shared_ptr< SymbolicExpr > &rhs, ComputationContext &context)
__attribute__((visibility("default"))) Result< EqvProfile > eqv_profile_from_name(const std const std::string & name
QuantityResult strip_unit(const std::shared_ptr< SymbolicExpr > &expression, UnitStripMode mode, ComputationContext &context)
QuantityResult convert_unit(const std::shared_ptr< SymbolicExpr > &expression, const std::string &target_unit, ComputationContext &context)
QuantityResult quantity_add(const std::shared_ptr< SymbolicExpr > &lhs, const std::shared_ptr< SymbolicExpr > &rhs, ComputationContext &context)
内部 AST 定义。