Numworks Epsilon  1.4.1
Graphing Calculator Operating System
trigonometry.cpp
Go to the documentation of this file.
3 #include <poincare/complex.h>
4 #include <poincare/symbol.h>
5 #include <poincare/undefined.h>
6 #include <poincare/rational.h>
8 #include <poincare/subtraction.h>
9 #include <poincare/derivative.h>
10 #include <ion.h>
11 extern "C" {
12 #include <assert.h>
13 }
14 #include <cmath>
15 #include <float.h>
16 
17 namespace Poincare {
18 
20  assert(e->numberOfOperands() == 1);
21  if (angleUnit == Expression::AngleUnit::Default) {
23  }
24  const Expression * op = e->operand(0);
25  int d = op->polynomialDegree('x');
26  // op is not linear so we cannot not easily find an interesting range
27  if (d < 0 || d > 1) {
28  return op->characteristicXRange(context, angleUnit);
29  }
30  // The expression e is x-independent
31  if (d == 0) {
32  return 0.0f;
33  }
34  // e has the form cos/sin/tan(ax+b) so it is periodic of period 2*Pi/a
35  assert(d == 1);
36  /* To compute a, the slope of the expression op, we compute the derivative of
37  * op for any x value. */
39  const Poincare::Expression * args[2] = {op, &x};
40  Poincare::Derivative derivative(args, true);
41  float a = derivative.approximateToScalar<float>(context);
42  float pi = angleUnit == Expression::AngleUnit::Radian ? M_PI : 180.0f;
43  return 2.0f*pi/std::fabs(a);
44 }
45 
48  Expression * lookup = Trigonometry::table(e->operand(0), e->type(), context, angleUnit);
49  if (lookup != nullptr) {
50  return e->replaceWith(lookup, true);
51  }
53  if (e->operand(0)->type() == correspondingType) {
54  float trigoOp = e->operand(0)->operand(0)->approximateToScalar<float>(context, angleUnit);
55  if (e->type() == Expression::Type::Tangent || (trigoOp >= -1.0f && trigoOp <= 1.0f)) {
56  return e->replaceWith(e->editableOperand(0)->editableOperand(0), true);
57  }
58  }
59  if (e->operand(0)->sign() == Expression::Sign::Negative) {
60  Expression * op = e->editableOperand(0);
61  Expression * newOp = op->setSign(Expression::Sign::Positive, context, angleUnit);
62  newOp->shallowReduce(context, angleUnit);
63  if (e->type() == Expression::Type::Cosine) {
64  return e->shallowReduce(context, angleUnit);
65  } else {
66  Multiplication * m = new Multiplication(new Rational(-1), e->clone(), false);
67  m->editableOperand(1)->shallowReduce(context, angleUnit);
68  return e->replaceWith(m, true)->shallowReduce(context, angleUnit);
69  }
70  }
71  if ((angleUnit == Expression::AngleUnit::Radian && e->operand(0)->type() == Expression::Type::Multiplication && e->operand(0)->numberOfOperands() == 2 && e->operand(0)->operand(1)->type() == Expression::Type::Symbol && static_cast<const Symbol *>(e->operand(0)->operand(1))->name() == Ion::Charset::SmallPi && e->operand(0)->operand(0)->type() == Expression::Type::Rational) || (angleUnit == Expression::AngleUnit::Degree && e->operand(0)->type() == Expression::Type::Rational)) {
72  Rational * r = angleUnit == Expression::AngleUnit::Radian ? static_cast<Rational *>(e->editableOperand(0)->editableOperand(0)) : static_cast<Rational *>(e->editableOperand(0));
73  int unaryCoefficient = 1; // store 1 or -1
74  // Replace argument in [0, Pi/2[ or [0, 90[
76  Integer dividand = angleUnit == Expression::AngleUnit::Radian ? Integer::Addition(r->numerator(), r->numerator()) : r->numerator();
77  if (divisor.isLowerThan(dividand)) {
79  IntegerDivision div = Integer::Division(r->numerator(), piDivisor);
80  dividand = angleUnit == Expression::AngleUnit::Radian ? Integer::Addition(div.remainder, div.remainder) : div.remainder;
81  if (divisor.isLowerThan(dividand)) {
82  div.remainder = Integer::Subtraction(piDivisor, div.remainder);
84  unaryCoefficient *= -1;
85  }
86  }
87  Rational * newR = new Rational(div.remainder, r->denominator());
88  Expression * rationalParent = angleUnit == Expression::AngleUnit::Radian ? e->editableOperand(0) : e;
89  rationalParent->replaceOperand(r, newR, true);
90  e->editableOperand(0)->shallowReduce(context, angleUnit);
91  if (Integer::Division(div.quotient, Integer(2)).remainder.isOne() && e->type() != Expression::Type::Tangent) {
92  unaryCoefficient *= -1;
93  }
94  Expression * simplifiedCosine = e->shallowReduce(context, angleUnit); // recursive
95  Multiplication * m = new Multiplication(new Rational(unaryCoefficient), simplifiedCosine->clone(), false);
96  return simplifiedCosine->replaceWith(m, true)->shallowReduce(context, angleUnit);
97  }
99  assert(!divisor.isLowerThan(dividand));
100  }
101  return e;
102 }
103 
106  if (e->type() == Expression::Type::Multiplication && e->operand(1)->type() == Expression::Type::Sine && e->operand(0)->type() == Expression::Type::Power && e->operand(0)->operand(0)->type() == Expression::Type::Cosine && e->operand(0)->operand(1)->type() == Expression::Type::Rational && static_cast<const Rational *>(e->operand(0)->operand(1))->isMinusOne()) {
107  return true;
108  }
109  return false;
110 }
111 
114  if (e->type() != Expression::Type::ArcTangent) {
115  float approxOp = e->operand(0)->approximateToScalar<float>(context, angleUnit);
116  if (approxOp > 1.0f || approxOp < -1.0f) {
117  return e->replaceWith(new Undefined(), true);
118  }
119  }
121  float pi = angleUnit == Expression::AngleUnit::Radian ? M_PI : 180;
122  if (e->operand(0)->type() == correspondingType) {
123  float trigoOp = e->operand(0)->operand(0)->approximateToScalar<float>(context, angleUnit);
124  if ((e->type() == Expression::Type::ArcCosine && trigoOp >= 0.0f && trigoOp <= pi) ||
125  (e->type() == Expression::Type::ArcSine && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) ||
126  (e->type() == Expression::Type::ArcTangent && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f)) {
127  return e->replaceWith(e->editableOperand(0)->editableOperand(0), true);
128  }
129  }
130  // Special case for arctan(sin(x)/cos(x))
132  float trigoOp = e->operand(0)->operand(1)->operand(0)->approximateToScalar<float>(context, angleUnit);
133  if (trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) {
134  return e->replaceWith(e->editableOperand(0)->editableOperand(1)->editableOperand(0), true);
135  }
136  }
137  Expression * lookup = Trigonometry::table(e->operand(0), e->type(), context, angleUnit);
138  if (lookup != nullptr) {
139  return e->replaceWith(lookup, true);
140  }
141  // arccos(-x) = Pi-arcos(x), arcsin(-x) = -arcsin(x), arctan(-x)=-arctan(x)
142  if (e->operand(0)->sign() == Expression::Sign::Negative || (e->operand(0)->type() == Expression::Type::Multiplication && e->operand(0)->operand(0)->type() == Expression::Type::Rational && static_cast<const Rational *>(e->operand(0)->operand(0))->isMinusOne())) {
143  Expression * op = e->editableOperand(0);
144  if (e->operand(0)->sign() == Expression::Sign::Negative) {
145  Expression * newOp = op->setSign(Expression::Sign::Positive, context, angleUnit);
146  newOp->shallowReduce(context, angleUnit);
147  } else {
148  ((Multiplication *)op)->removeOperand(op->editableOperand(0), true);
149  op->shallowReduce(context, angleUnit);
150  }
151  if (e->type() == Expression::Type::ArcCosine) {
152  Expression * pi = angleUnit == Expression::AngleUnit::Radian ? static_cast<Expression *>(new Symbol(Ion::Charset::SmallPi)) : static_cast<Expression *>(new Rational(180));
153  Subtraction * s = new Subtraction(pi, e->clone(), false);
154  s->editableOperand(1)->shallowReduce(context, angleUnit);
155  return e->replaceWith(s, true)->shallowReduce(context, angleUnit);
156  } else {
157  Multiplication * m = new Multiplication(new Rational(-1), e->clone(), false);
158  m->editableOperand(1)->shallowReduce(context, angleUnit);
159  return e->replaceWith(m, true)->shallowReduce(context, angleUnit);
160  }
161  }
162 
163  return e;
164 }
165 
166 static_assert('\x89' == Ion::Charset::SmallPi, "Unicode error");
167 constexpr const char * cheatTable[Trigonometry::k_numberOfEntries][5] =
168 {{"-90", "\x89*(-2)^(-1)", "", "-1", "undef"},
169  {"-75", "\x89*(-5)*12^(-1)", "", "(-1)*6^(1/2)*4^(-1)-2^(1/2)*4^(-1)", "-(3^(1/2)+2)"},
170  {"-72", "\x89*2*(-5)^(-1)", "", "-(5/8+5^(1/2)/8)^(1/2)", "-(5+2*5^(1/2))^(1/2)"},
171  {"-135/2", "\x89*(-3)*8^(-1)", "", "-(2+2^(1/2))^(1/2)*2^(-1)", "-1-2^(1/2)"},
172  {"-60", "\x89*(-3)^(-1)", "", "-3^(1/2)*2^(-1)", "-3^(1/2)"},
173  {"-54", "\x89*(-3)*10^(-1)", "", "4^(-1)*(-1-5^(1/2))", "-(1+2*5^(-1/2))^(1/2)"},
174  {"-45", "\x89*(-4)^(-1)", "", "(-1)*(2^(-1/2))", "-1"},
175  {"-36", "\x89*(-5)^(-1)", "", "-(5/8-5^(1/2)/8)^(1/2)", "-(5-2*5^(1/2))^(1/2)"},
176  {"-30", "\x89*(-6)^(-1)", "", "-0.5", "-3^(-1/2)"},
177  {"-45/2", "\x89*(-8)^(-1)", "", "(2-2^(1/2))^(1/2)*(-2)^(-1)", "1-2^(1/2)"},
178  {"-18", "\x89*(-10)^(-1)", "", "4^(-1)*(1-5^(1/2))", "-(1-2*5^(-1/2))^(1/2)"},
179  {"-15", "\x89*(-12)^(-1)", "", "-6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "3^(1/2)-2"},
180  {"0", "0", "1", "0", "0"},
181  {"15", "\x89*12^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*(-4)^(-1)", "-(3^(1/2)-2)"},
182  {"18", "\x89*10^(-1)", "(5/8+5^(1/2)/8)^(1/2)", "4^(-1)*(5^(1/2)-1)", "(1-2*5^(-1/2))^(1/2)"},
183  {"45/2", "\x89*8^(-1)", "(2+2^(1/2))^(1/2)*2^(-1)", "(2-2^(1/2))^(1/2)*2^(-1)", "2^(1/2)-1"},
184  {"30", "\x89*6^(-1)", "3^(1/2)*2^(-1)", "0.5", "3^(-1/2)"},
185  {"36", "\x89*5^(-1)", "(5^(1/2)+1)*4^(-1)", "(5/8-5^(1/2)/8)^(1/2)", "(5-2*5^(1/2))^(1/2)"},
186  {"45", "\x89*4^(-1)", "2^(-1/2)", "2^(-1/2)", "1"},
187  {"54", "\x89*3*10^(-1)", "(5/8-5^(1/2)/8)^(1/2)", "4^(-1)*(5^(1/2)+1)", "(1+2*5^(-1/2))^(1/2)"},
188  {"60", "\x89*3^(-1)", "0.5", "3^(1/2)*2^(-1)", "3^(1/2)"},
189  {"135/2", "\x89*3*8^(-1)", "(2-2^(1/2))^(1/2)*2^(-1)", "(2+2^(1/2))^(1/2)*2^(-1)", "1+2^(1/2)"},
190  {"72", "\x89*2*5^(-1)", "(5^(1/2)-1)*4^(-1)", "(5/8+5^(1/2)/8)^(1/2)", "(5+2*5^(1/2))^(1/2)"},
191  {"75", "\x89*5*12^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*(-4)^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "3^(1/2)+2"},
192  {"90", "\x89*2^(-1)", "0", "1", "undef"},
193  {"105", "\x89*7*12^(-1)", "-6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "", ""},
194  {"108", "\x89*3*5^(-1)", "(1-5^(1/2))*4^(-1)", "", ""},
195  {"225/2", "\x89*5*8^(-1)", "(2-2^(1/2))^(1/2)*(-2)^(-1)", "", ""},
196  {"120", "\x89*2*3^(-1)", "-0.5", "", ""},
197  {"126", "\x89*7*10^(-1)", "-(5*8^(-1)-5^(1/2)*8^(-1))^(1/2)", "", ""},
198  {"135", "\x89*3*4^(-1)", "(-1)*(2^(-1/2))", "", ""},
199  {"144", "\x89*4*5^(-1)", "(-5^(1/2)-1)*4^(-1)", "", ""},
200  {"150", "\x89*5*6^(-1)", "-3^(1/2)*2^(-1)", "", ""},
201  {"315/2", "\x89*7*8^(-1)", "-(2+2^(1/2))^(1/2)*2^(-1)", "", ""},
202  {"162", "\x89*9*10^(-1)", "-(5*8^(-1)+5^(1/2)*8^(-1))^(1/2)", "", ""},
203  {"165", "\x89*11*12^(-1)", "(-1)*6^(1/2)*4^(-1)-2^(1/2)*4^(-1)", "", ""},
204  {"180", "\x89", "-1", "0", "0"}};
205 
208  int angleUnitIndex = angleUnit == Expression::AngleUnit::Radian ? 1 : 0;
209  int trigonometricFunctionIndex = type == Expression::Type::Cosine || type == Expression::Type::ArcCosine ? 2 : (type == Expression::Type::Sine || type == Expression::Type::ArcSine ? 3 : 4);
210  int inputIndex = type == Expression::Type::ArcCosine || type == Expression::Type::ArcSine || type == Expression::Type::ArcTangent ? trigonometricFunctionIndex : angleUnitIndex;
211  int outputIndex = type == Expression::Type::ArcCosine || type == Expression::Type::ArcSine || type == Expression::Type::ArcTangent ? angleUnitIndex : trigonometricFunctionIndex;
212 
213  /* Avoid looping if we can exclude quickly that the e is in the table */
214  if (inputIndex == 0 && e->type() != Expression::Type::Rational) {
215  return nullptr;
216  }
217  if (inputIndex == 1 && e->type() != Expression::Type::Rational && e->type() != Expression::Type::Multiplication && e->type() != Expression::Type::Symbol) {
218  return nullptr;
219  }
221  return nullptr;
222  }
223  for (int i = 0; i < k_numberOfEntries; i++) {
224  Expression * input = Expression::parse(cheatTable[i][inputIndex]);
225  if (input == nullptr) {
226  continue;
227  }
228  Expression::Reduce(&input, context, angleUnit);
229  bool rightInput = input->isIdenticalTo(e);
230  delete input;
231  if (rightInput) {
232  Expression * output = Expression::parse(cheatTable[i][outputIndex]);
233  if (output == nullptr) {
234  return nullptr;
235  }
236  Expression::Reduce(&output, context, angleUnit);
237  return output;
238  }
239  }
240  return nullptr;
241 }
242 
243 }
static float characteristicXRange(const Expression *e, Context &context, Expression::AngleUnit angleUnit)
static Preferences * sharedPreferences()
Definition: preferences.cpp:14
#define assert(e)
Definition: assert.h:9
constexpr const char * cheatTable[Trigonometry::k_numberOfEntries][5]
Expression * replaceWith(Expression *newOperand, bool deleteAfterReplace=true)
Definition: expression.cpp:85
#define M_PI
Definition: math.h:17
Expression::AngleUnit angleUnit() const
Definition: preferences.cpp:19
#define fabs(x)
Definition: math.h:178
virtual Expression * clone() const =0
const Integer denominator() const
Definition: rational.cpp:59
static Expression * parse(char const *string)
Definition: expression.cpp:25
bool isLowerThan(const Integer &other) const
Definition: integer.cpp:226
Sign sign() const override
Definition: rational.cpp:72
Expression * editableOperand(int i)
Definition: expression.h:176
virtual int numberOfOperands() const =0
bool isIdenticalTo(const Expression *e) const
Definition: expression.h:219
static Integer Addition(const Integer &i, const Integer &j)
Definition: integer.cpp:232
static bool ExpressionIsEquivalentToTangent(const Expression *e)
const Integer numerator() const
Definition: rational.cpp:55
args
Definition: i18n.py:175
static Expression * shallowReduceDirectFunction(Expression *e, Context &context, Expression::AngleUnit angleUnit)
static Complex< T > Float(T x)
Definition: complex.cpp:23
static Integer Subtraction(const Integer &i, const Integer &j)
Definition: integer.cpp:236
virtual float characteristicXRange(Context &context, AngleUnit angleUnit=AngleUnit::Default) const
Definition: expression.cpp:179
static constexpr int k_numberOfEntries
Definition: trigonometry.h:18
virtual Sign sign() const
Definition: expression.h:195
constexpr Event Multiplication
Definition: events.h:101
static Expression * table(const Expression *e, Expression::Type type, Context &context, Expression::AngleUnit angleUnit)
static Expression * shallowReduceInverseFunction(Expression *e, Context &context, Expression::AngleUnit angleUnit)
T approximateToScalar(Context &context, AngleUnit angleUnit=AngleUnit::Default) const
Definition: expression.cpp:347
static Integer Multiplication(const Integer &i, const Integer &j)
Definition: integer.cpp:240
const Expression * operand(int i) const
Definition: expression.cpp:78
static IntegerDivision Division(const Integer &numerator, const Integer &denominator)
Definition: integer.cpp:281
virtual int polynomialDegree(char symbolName) const
Definition: expression.cpp:202
virtual Type type() const =0
void replaceOperand(const Expression *oldOperand, Expression *newOperand, bool deleteOldOperand=true)
Definition: expression.cpp:91