-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathequation.cpp
More file actions
264 lines (243 loc) · 7.8 KB
/
Copy pathequation.cpp
File metadata and controls
264 lines (243 loc) · 7.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
#include "equation.hpp"
#include <iostream>
#include <cmath>
#include <algorithm>
#include <numeric>
// Copied from cppreference, not sure why its not in the std namespace already.
// This lets std::visit take a set of lambdas.
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
// The two cast "operators" for the Operator type.
Equation::Operator constexpr Equation::to_Operator(char const c){
switch(c){
case '+': return Operator::ADD;
case '-': return Operator::SUBTRACT;
case '*': return Operator::MULTIPLY;
case '/': return Operator::DIVIDE;
case '^': return Operator::EXPONENT;
default: throw "Invalid Operator"; // TODO: That's not very constexper of you.
}
}
char constexpr Equation::to_sym(Operator const op){
switch(op){
case Operator::ADD: return '+';
case Operator::SUBTRACT: return '-';
case Operator::MULTIPLY: return '*';
case Operator::DIVIDE: return '/';
case Operator::EXPONENT: return '^';
}
}
//
// Fixing the semantics of "unique_ptr" until I switch to a COW.
//
Equation::Op_node::Op_node(Operator const _op,Equation const& _l,Equation const& _r)
:op(_op){
left=std::make_unique<Equation>(_l); // Not sure why this isn't just the constructor
right=std::make_unique<Equation>(_r);
}
Equation::Op_node::Op_node(char const _op,Equation const& _l,Equation const& _r)
:op(Equation::to_Operator(_op)){
left=std::make_unique<Equation>(_l); // Not sure why this isn't just the constructor
right=std::make_unique<Equation>(_r);
}
Equation::Op_node::Op_node(Op_node const& eq)
:op(eq.op){
left=std::make_unique<Equation>(*eq.left); // Not sure why this isn't just the constructor
right=std::make_unique<Equation>(*eq.right);
}
Equation::Op_node& Equation::Op_node::operator=(Equation::Op_node const& rhs){
op=rhs.op;
left=std::make_unique<Equation>(*rhs.left);
right=std::make_unique<Equation>(*rhs.right);
return *this;
}
// The actual things that belong in this file.
int precedent(Equation::Operator op){
switch(op){
case Equation::Operator::ADD: case Equation::Operator::SUBTRACT:
return 1;
case Equation::Operator::MULTIPLY: case Equation::Operator::DIVIDE:
return 2;
case Equation::Operator::EXPONENT:
return 3;
}}
bool commutative(Equation::Operator op){
switch(op){
case Equation::Operator::ADD:
case Equation::Operator::MULTIPLY:
return true;
case Equation::Operator::SUBTRACT:
case Equation::Operator::DIVIDE:
case Equation::Operator::EXPONENT:
return false;
}}
bool right_binding(Equation::Operator op){
// If this operator binds to it's right argument more strongly than
// it's left, then `a/(b/c)` needs parenthesizes to not be
// misinterpreted.
switch(op){
case Equation::Operator::ADD:
case Equation::Operator::MULTIPLY:
case Equation::Operator::EXPONENT:
return false;
case Equation::Operator::SUBTRACT:
case Equation::Operator::DIVIDE:
return true;
}}
// This is a bad TypeDef; but, it's only used for the immediately following.
typedef std::tuple<std::vector<Equation::Variable>,std::vector<Equation::Variable>,std::vector<Equation::Variable>> TVvVvVv;
TVvVvVv operator+(TVvVvVv const lhs,TVvVvVv const rhs){
auto& [lu,lb,lm] = lhs;
auto& [ru,rb,rm] = rhs;
std::vector<Equation::Variable> u,b,m;
for(auto&e:lm)
m.push_back(e);
for(auto&e:rm)
if(std::find(m.begin(), m.end(),e) == m.end())
m.push_back(e);
for(auto&e:lb)
if(std::find(m.begin(), m.end(),e) == m.end())
if(std::find(ru.begin(), ru.end(),e) == ru.end())
b.push_back(e);
else
m.push_back(e);
for(auto&e:rb)
if(std::find(m.begin(), m.end(),e) == m.end())
if(std::find(lu.begin(), lu.end(),e) == lu.end()){
if(std::find(b.begin(), b.end(),e) == b.end())
b.push_back(e);
}else
m.push_back(e);
for(auto&e:lu)
if(std::find(m.begin(), m.end(),e) == m.end())
u.push_back(e);
for(auto&e:ru)
if(std::find(m.begin(), m.end(),e) == m.end())
if(std::find(u.begin(), u.end(),e) == u.end())
u.push_back(e);
return {u,b,m};
}
TVvVvVv
bindings(Equation const& eq){
return std::visit(overloaded{
[&]( double)->TVvVvVv{ return {{},{},{}}; },
[&]( Equation::Variable var)->TVvVvVv{ return {{var},{},{}}; },
[&]( Equation::Constant)->TVvVvVv{ return {{},{},{}}; },
[&]( Equation::F_node const& f)->TVvVvVv{
auto body=std::transform_reduce(f.arguments.begin(),
f.arguments.end(),
TVvVvVv({{},{},{}}),
std::plus{},
bindings);
// There's only ever one of these, this is just the closes to
// a "maybe" monad I have right now.
auto super=std::transform_reduce(f.superscript.begin(),
f.superscript.end(),
TVvVvVv({{},{},{}}),
std::plus{},
bindings);
auto sub=std::transform_reduce(f.subscript.begin(),
f.subscript.end(),
TVvVvVv({{},{},{}}),
std::plus{},
bindings);
if(f.bound){
auto var = *f.bound;
std::erase(std::get<0>(body),var);
return body+TVvVvVv({{},{var},{}})+super+sub;
}
return body+super+sub;
},
[&](Equation::Op_node const& eq)->TVvVvVv{ return bindings(*eq.left)+bindings(*eq.right); }
},eq.value);
}
std::ostream& operator<<(std::ostream& o,Equation const& rhs){
std::visit(overloaded{
[&](double val){
if(std::isinf(val))
if(val>0)
o<<"\\infty";
else
o<<"-\\infty";
else if(std::isnan(val))
o<<"NaN";
else
o << val;
},
[&](Equation::Variable var){o << var.name;},
[&](Equation::Constant cnst){
if(cnst.name=="pi")
o<<"\\pi";
else if(cnst.name=="phi")
o<<"\\phi";
else if(cnst.name=="i")
o<<"i";
else
o<<"[New Constant, Fix Printer]";
},
[&](Equation::F_node const& f){
auto parens=false;
if(f.function=="binding"){
o<<'['<<f.arguments[0]<<']';
if(f.superscript.size())
o<<"^{"<<*f.bound<<"="<<f.superscript[0]<<"}";
o<<"_{"<<*f.bound<<"="<<f.subscript[0]<<"}";
}else{
o << f.function;
if(f.bound){
o<<"_{"<<*f.bound<<"="<<f.subscript[0]<<"}";
if(f.superscript.size()){
if(auto eqq=std::get_if<Equation::Op_node>(&f.superscript[0].value))
parens=true;
o<<"^";
if(parens)o<<"{";
o<<f.superscript[0];
if(parens)o<<"}";
}
o<<"("<<f.arguments[0]<<")";
}else{
if(f.subscript.size()){
if(auto eqq=std::get_if<Equation::Op_node>(&f.subscript[0].value))
parens=true;
else if(auto eqn=std::get_if<double>(&f.subscript[0].value))
if(*eqn<0) // The minus sign needs {}s in LaTeX
parens=true;
o<<"_";
if(parens)
o<<"{";
o <<f.subscript[0];
if(parens)
o<<"}";
}
if(f.customizations.size())
o<<'['<<f.customizations[0]<<']';
if(f.arguments.size())
o<<'{'<<f.arguments[0]<<'}';
}
}
},
[&](Equation::Op_node const& eq){
auto prec = precedent(eq.op);
bool add_parentheses = false;
if (auto* eqq=std::get_if<Equation::Op_node>(&eq.left->value))
add_parentheses = precedent(eqq->op)<prec;
if (add_parentheses)
o << '(';
o<<*eq.left;
if (add_parentheses)
o << ')';
o << Equation::to_sym(eq.op);
add_parentheses = false;
if (auto* eqq=std::get_if<Equation::Op_node>(&eq.right->value))
add_parentheses = precedent(eqq->op)<prec || (precedent(eqq->op)==prec && right_binding(eq.op));
if (add_parentheses)
o << '(';
o<< *eq.right;
if (add_parentheses)
o << ')';
}
},rhs.value);
return o;
}