Skip to content

Commit 1eb1b7e

Browse files
authored
use PARI wrappers in plain formulas (#313)
1 parent f398f14 commit 1eb1b7e

File tree

5 files changed

+49
-39
lines changed

5 files changed

+49
-39
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ To install or update LODA, please follow the [installation instructions](https:/
88

99
### Enhancements
1010

11+
* Use floor/truncate functions in plain formulas
1112
* Simplify formulas using Gaussian elimination
1213
* Simplify sums and products in formulas
1314
* Extended internal maintenance command

src/form/expression_util.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,10 @@ bool ExpressionUtil::canBeNegative(const Expression& e) {
298298
std::any_of(e.children.begin(), e.children.end(),
299299
[](const Expression& c) { return !canBeNegative(c); })) {
300300
return false;
301+
} else if (e.name == "binomial") {
302+
break; // infer from children
301303
} else {
302-
return true;
304+
return true; // unknown function
303305
}
304306
case Expression::Type::POWER:
305307
if (e.children.size() == 2 &&

src/form/formula_gen.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ Expression FormulaGenerator::operandToExpression(Operand op) const {
5757
throw std::runtime_error("internal error"); // unreachable
5858
}
5959

60+
Expression divToFraction(const Expression& numerator,
61+
const Expression& denominator) {
62+
Expression frac(Expression::Type::FRACTION, "", {numerator, denominator});
63+
std::string func = "floor";
64+
if (ExpressionUtil::canBeNegative(numerator) ||
65+
ExpressionUtil::canBeNegative(denominator)) {
66+
func = "truncate";
67+
}
68+
Expression wrapper(Expression::Type::FUNCTION, func, {frac});
69+
return wrapper;
70+
}
71+
6072
bool FormulaGenerator::update(const Operation& op) {
6173
auto source = operandToExpression(op.source);
6274
auto target = operandToExpression(op.target);
@@ -89,15 +101,34 @@ bool FormulaGenerator::update(const Operation& op) {
89101
break;
90102
}
91103
case Operation::Type::DIV: {
92-
res = Expression(Expression::Type::FRACTION, "", {prevTarget, source});
104+
res = divToFraction(prevTarget, source);
93105
break;
94106
}
95107
case Operation::Type::POW: {
96108
res = Expression(Expression::Type::POWER, "", {prevTarget, source});
109+
if (ExpressionUtil::canBeNegative(source)) {
110+
Expression wrapper(Expression::Type::FUNCTION, "truncate", {res});
111+
res = wrapper;
112+
}
97113
break;
98114
}
99115
case Operation::Type::MOD: {
100-
res = Expression(Expression::Type::MODULUS, "", {prevTarget, source});
116+
auto c1 = prevTarget;
117+
auto c2 = source;
118+
if (ExpressionUtil::canBeNegative(c1) ||
119+
ExpressionUtil::canBeNegative(c2)) {
120+
Expression wrapper(Expression::Type::SUM);
121+
wrapper.newChild(c1);
122+
wrapper.newChild(Expression::Type::PRODUCT);
123+
auto frac = divToFraction(c1, c2);
124+
wrapper.children[1].newChild(
125+
Expression(Expression::Type::CONSTANT, "", Number(-1)));
126+
wrapper.children[1].newChild(c2);
127+
wrapper.children[1].newChild(frac);
128+
res = wrapper;
129+
} else {
130+
res = Expression(Expression::Type::MODULUS, "", {c1, c2});
131+
}
101132
break;
102133
}
103134
case Operation::Type::BIN: {

src/form/pari.cpp

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,31 +43,7 @@ bool convertExprToPari(Expression& expr) {
4343
return false;
4444
}
4545
}
46-
if (expr.type == Expression::Type::FRACTION) {
47-
convertFracToPari(expr);
48-
} else if (expr.type == Expression::Type::POWER) {
49-
if (ExpressionUtil::canBeNegative(expr.children.at(1))) {
50-
Expression wrapper(Expression::Type::FUNCTION, "truncate", {expr});
51-
expr = wrapper;
52-
}
53-
} else if (expr.type == Expression::Type::MODULUS) {
54-
auto c1 = expr.children.at(0);
55-
auto c2 = expr.children.at(1);
56-
if (ExpressionUtil::canBeNegative(c1) ||
57-
ExpressionUtil::canBeNegative(c2)) {
58-
Expression wrapper(Expression::Type::SUM);
59-
wrapper.newChild(c1);
60-
wrapper.newChild(Expression::Type::PRODUCT);
61-
Expression frac(Expression::Type::FRACTION, "", {c1, c2});
62-
convertFracToPari(frac);
63-
wrapper.children[1].newChild(
64-
Expression(Expression::Type::CONSTANT, "", Number(-1)));
65-
wrapper.children[1].newChild(c2);
66-
wrapper.children[1].newChild(frac);
67-
expr = wrapper;
68-
}
69-
} else if (expr.type == Expression::Type::FUNCTION &&
70-
expr.name == "binomial") {
46+
if (expr.type == Expression::Type::FUNCTION && expr.name == "binomial") {
7147
// TODO: check feedback from PARI team to avoid this limitation
7248
if (ExpressionUtil::canBeNegative(expr.children.at(1))) {
7349
return false;

tests/formula/program-formula.txt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
A000004: a(n) = 0
2-
A000008: a(n) = b(n+1), b(n) = b(n-1)+binomial((n+1)/2-((2*n+2)/5)+1,2), b(0) = 0
2+
A000008: a(n) = b(n+1), b(n) = b(n-1)+binomial(-floor((2*n+2)/5)+floor((n+1)/2)+1,2), b(0) = 0
33
A000012: a(n) = 1
44
A000023: a(n) = (n-1)*(2*a(n-2)+a(n-1))-a(n-1), a(3) = -2, a(2) = 2, a(1) = -1, a(0) = 1
55
A000027: a(n) = n+1
@@ -12,9 +12,9 @@ A000073: a(n) = a(n-1)+a(n-2)+a(n-3), a(2) = 1, a(1) = 0, a(0) = 0
1212
A000096: a(n) = binomial(n+2,2)-1
1313
A000142: a(n) = n*a(n-1), a(0) = 1
1414
A000165: a(n) = b(2*n), b(n) = n*b(n-2), b(1) = 1, b(0) = 1
15-
A000168: a(n) = (2*A151383(n))/(n+2), A151383(n) = ((binomial(2*n,n)/(n+1))*3^(n+1))/3
16-
A000212: a(n) = (n^2)/3
17-
A000276: a(n) = (2*c(n+1)*(n+3))/2, b(n) = b(n-1)*(n+1), b(2) = 6, b(1) = 2, b(0) = 1, c(n) = c(n-1)*(n+1)+b(n-1), c(2) = 5, c(1) = 1, c(0) = 0
15+
A000168: a(n) = truncate((2*A151383(n))/(n+2)), A151383(n) = truncate((floor(binomial(2*n,n)/(n+1))*3^(n+1))/3)
16+
A000212: a(n) = floor((n^2)/3)
17+
A000276: a(n) = truncate((2*c(n+1)*(n+3))/2), b(n) = b(n-1)*(n+1), b(2) = 6, b(1) = 2, b(0) = 1, c(n) = c(n-1)*(n+1)+b(n-1), c(2) = 5, c(1) = 1, c(0) = 0
1818
A000278: a(n) = (-a(n-2))^2+a(n-1), a(1) = 1, a(0) = 0
1919
A000280: a(n) = a(n-2)^3+a(n-1), a(1) = 1, a(0) = 0
2020
A000284: a(n) = a(n-1)^3+a(n-2), a(3) = 2, a(2) = 1, a(1) = 1, a(0) = 0
@@ -23,17 +23,17 @@ A000290: a(n) = n^2
2323
A000295: a(n) = 2^n-n-1
2424
A000321: a(n) = -a(n-2)*(2*n-2)-a(n-1), a(2) = -1, a(1) = -1, a(0) = 1
2525
A000407: a(n) = a(n-1)*(4*n+2), a(1) = 6, a(0) = 1
26-
A000463: a(n) = gcd(n+1,n/2+1)*(n/2+1)
26+
A000463: a(n) = gcd(n+1,floor(n/2)+1)*(floor(n/2)+1)
2727
A000466: a(n) = 4*n^2-1
2828
A000538: a(n) = n^4+a(n-1), a(0) = 0
2929
A000561: a(n) = (n+3)*(binomial(-3*n-1,2)+1)
30-
A000601: a(n) = b(n+4), b(n) = ((n-2)^2)/4+b(n-3), b(2) = 0, b(1) = 0, b(0) = 0
30+
A000601: a(n) = b(n+4), b(n) = b(n-3)+floor(((n-2)^2)/4), b(2) = 0, b(1) = 0, b(0) = 0
3131
A000748: a(n) = -3*a(n-1)-3*a(n-2), a(2) = 6, a(1) = -3, a(0) = 1
3232
A000792: a(n) = min(n+1,(n+1)%3)*c(n+1)+b(n+1), b(n) = 2*b(n-3)+2*c(n-3), b(5) = 2, b(4) = 2, b(3) = 2, b(2) = 1, b(1) = 1, b(0) = 1, c(n) = b(n-3)+c(n-3), c(5) = 1, c(4) = 1, c(3) = 1, c(2) = 0, c(1) = 0, c(0) = 0
3333
A000918: a(n) = 2^n-2
3434
A000931: a(n) = a(n-2)+a(n-3), a(5) = 1, a(4) = 0, a(3) = 1, a(2) = 0, a(1) = 0, a(0) = 1
3535
A000932: a(n) = n*a(n-2)+a(n-1), a(2) = 3, a(1) = 1, a(0) = 1
36-
A000933: a(n) = (b(max(n-2,0))+5)/6, b(n) = b(n-1)+n-1, b(0) = 0
36+
A000933: a(n) = truncate((b(max(n-2,0))+5)/6), b(n) = b(n-1)+n-1, b(0) = 0
3737
A001044: a(n) = A000142(n)^2, A000142(n) = n*A000142(n-1), A000142(0) = 1
3838
A001054: a(n) = a(n-2)*a(n-1)-1, a(3) = -2, a(2) = -1, a(1) = 1, a(0) = 0
3939
A001107: a(n) = n*(4*n-3)
@@ -48,7 +48,7 @@ A001542: a(n) = 3*a(n-1)+2*b(n-1), a(1) = 2, a(0) = 0, b(n) = 4*a(n-1)+3*b(n-1),
4848
A001611: a(n) = A000045(n)+1, A000045(n) = A000045(n-1)+A000045(n-2), A000045(1) = 1, A000045(0) = 0
4949
A001630: a(n) = 2*a(n-1)-a(n-5), a(6) = 12, a(5) = 6, a(4) = 3, a(3) = 2, a(2) = 1, a(1) = 0, a(0) = 0
5050
A001687: a(n) = a(n-2)+a(n-5), a(5) = 1, a(4) = 0, a(3) = 1, a(2) = 0, a(1) = 1, a(0) = 0
51-
A001715: a(n) = b(n+3)/6, b(n) = n*b(n-1), b(0) = 1
51+
A001715: a(n) = truncate(b(n+3)/6), b(n) = n*b(n-1), b(0) = 1
5252
A001906: a(n) = 3*a(n-1)-a(n-2), a(3) = 8, a(2) = 3, a(1) = 1, a(0) = 0
5353
A001911: a(n) = b(n)-2, b(n) = b(n-1)+b(n-2), b(1) = 3, b(0) = 2
5454
A001923: a(n) = n^n+a(n-1), a(0) = 0
@@ -59,14 +59,14 @@ A004273: a(n) = max(2*n-1,0)
5959
A004277: a(n) = max(2*n,1)
6060
A005408: a(n) = 2*n+1
6161
A005843: a(n) = 2*n
62-
A007583: a(n) = 2*((4^n)/3)+1
62+
A007583: a(n) = 2*floor((4^n)/3)+1
6363
A008785: a(n) = (n+4)^n
6464
A008999: a(n) = 2*a(n-1)+a(n-4), a(4) = 17, a(3) = 8, a(2) = 4, a(1) = 2, a(0) = 1
6565
A014731: a(n) = 4*b(n)^2, b(n) = 4*b(n-1)+b(n-2), b(1) = -2, b(0) = -1
6666
A021019: a(n) = 6*min(n,1)
6767
A022322: a(n) = b(n+2), b(n) = c(n-1), b(2) = 1, b(1) = 6, b(0) = 0, c(n) = c(n-1)+c(n-2)+1, c(3) = 10, c(2) = 8, c(1) = 1, c(0) = 6
6868
A022958: a(n) = -n+2
69-
A037536: a(n) = b(n+1), b(n) = (3*c(n-1))/13, b(1) = 1, b(0) = 0, c(n) = 3*c(n-1), c(1) = 24, c(0) = 8
69+
A037536: a(n) = b(n+1), b(n) = truncate((3*c(n-1))/13), b(1) = 1, b(0) = 0, c(n) = 3*c(n-1), c(1) = 24, c(0) = 8
7070
A048745: a(n) = 2*a(n-1)+a(n-2)+3, a(1) = 5, a(0) = 1
7171
A062815: a(n) = b(n+1), b(n) = n*n^n+b(n-1), b(0) = 0
7272
A078013: a(n) = b(n-2), a(2) = 0, a(1) = 0, a(0) = 1, b(n) = -b(n-3)+b(n-1), b(2) = -1, b(1) = -1, b(0) = 0
@@ -76,7 +76,7 @@ A123111: a(n) = ((n+1)^5+(n+1)^3+n+2)*(n+1)^2+1
7676
A134271: a(n) = b(max(n-2,0))+min(n-2,0), b(n) = 2*b(n-3)+b(n-2), b(2) = 4, b(1) = 0, b(0) = 2
7777
A154118: a(n) = 2*a(n-1)+5, a(1) = 2, a(0) = 1
7878
A157823: a(n) = (min(n-1,0)-1)*(b(max(n-1,0))+3)+3, b(n) = 2*b(n-1), b(0) = 1
79-
A173673: a(n) = (b(n)+min(n,n%2))/2, b(n) = b(n-2)+b(n-4), b(4) = 1, b(3) = 1, b(2) = 1, b(1) = 0, b(0) = 0
79+
A173673: a(n) = truncate((b(n)+min(n,n%2))/2), b(n) = b(n-2)+b(n-4), b(4) = 1, b(3) = 1, b(2) = 1, b(1) = 0, b(0) = 0
8080
A275698: a(n) = a(n-1)*(a(n-1)-3)+3, a(2) = 13, a(1) = 5, a(0) = 2
8181
A293006: a(n) = b(max(n-1,0)), b(n) = 2*b(n-1)+2*b(n-2)+4, b(1) = 2, b(0) = 0
8282
A318791: a(n) = (3*n-39)*(3*n-38)+41

0 commit comments

Comments
 (0)