Hoa central
Arithmetic.php
Go to the documentation of this file.
1 <?php
2 
37 namespace Hoa\Math\Visitor;
38 
39 use Hoa\Math;
40 use Hoa\Visitor;
41 
51 class Arithmetic implements Visitor\Visit
52 {
58  protected $_functions = null;
59 
65  protected $_constants = null;
66 
67 
68 
74  public function __construct()
75  {
76  $this->initializeConstants();
77  $this->initializeFunctions();
78 
79  return;
80  }
81 
90  public function visit(
91  Visitor\Element $element,
92  &$handle = null,
93  $eldnah = null
94  ) {
95  $type = $element->getId();
96  $children = $element->getChildren();
97 
98  if (null === $handle) {
99  $handle = function ($x) {
100  return $x;
101  };
102  }
103 
104  $acc = &$handle;
105 
106  switch ($type) {
107  case '#function':
108  $name = array_shift($children)->accept($this, $_, $eldnah);
109  $function = $this->getFunction($name);
110  $arguments = [];
111 
112  foreach ($children as $child) {
113  $child->accept($this, $_, $eldnah);
114  $arguments[] = $_();
115  unset($_);
116  }
117 
118  $acc = function () use ($function, $arguments, $acc) {
119  return $acc($function->distributeArguments($arguments));
120  };
121 
122  break;
123 
124  case '#negative':
125  $children[0]->accept($this, $a, $eldnah);
126 
127  $acc = function () use ($a, $acc) {
128  return $acc(-$a());
129  };
130 
131  break;
132 
133  case '#addition':
134  $children[0]->accept($this, $a, $eldnah);
135 
136  $acc = function ($b) use ($a, $acc) {
137  return $acc($a() + $b);
138  };
139 
140  $children[1]->accept($this, $acc, $eldnah);
141 
142  break;
143 
144  case '#substraction':
145  $children[0]->accept($this, $a, $eldnah);
146 
147  $acc = function ($b) use ($a, $acc) {
148  return $acc($a()) - $b;
149  };
150 
151  $children[1]->accept($this, $acc, $eldnah);
152 
153  break;
154 
155  case '#multiplication':
156  $children[0]->accept($this, $a, $eldnah);
157 
158  $acc = function ($b) use ($a, $acc) {
159  return $acc($a() * $b);
160  };
161 
162  $children[1]->accept($this, $acc, $eldnah);
163 
164  break;
165 
166  case '#division':
167  $children[0]->accept($this, $a, $eldnah);
168  $parent = $element->getParent();
169 
170  if (null === $parent ||
171  $type === $parent->getId()) {
172  $acc = function ($b) use ($a, $acc) {
173  if (0 === $b) {
174  throw new \RuntimeException(
175  'Division by zero is not possible.'
176  );
177  }
178 
179  return $acc($a()) / $b;
180  };
181  } else {
182  if ('#fakegroup' !== $parent->getId()) {
183  $classname = get_class($element);
184  $group = new $classname(
185  '#fakegroup',
186  null,
187  [$element],
188  $parent
189  );
190  $element->setParent($group);
191 
192  $this->visit($group, $acc, $eldnah);
193 
194  break;
195  } else {
196  $acc = function ($b) use ($a, $acc) {
197  if (0 === $b) {
198  throw new \RuntimeException(
199  'Division by zero is not possible.'
200  );
201  }
202 
203  return $acc($a() / $b);
204  };
205  }
206  }
207 
208  $children[1]->accept($this, $acc, $eldnah);
209 
210  break;
211 
212  case '#fakegroup':
213  case '#group':
214  $children[0]->accept($this, $a, $eldnah);
215 
216  $acc = function () use ($a, $acc) {
217  return $acc($a());
218  };
219 
220  break;
221 
222  case 'token':
223  $value = $element->getValueValue();
224  $out = null;
225 
226  if ('constant' === $element->getValueToken()) {
227  if (defined($value)) {
228  $out = constant($value);
229  } else {
230  $out = $this->getConstant($value);
231  }
232  } elseif ('id' === $element->getValueToken()) {
233  return $value;
234  } else {
235  $out = (float) $value;
236  }
237 
238  $acc = function () use ($out, $acc) {
239  return $acc($out);
240  };
241 
242  break;
243  }
244 
245  if (null === $element->getParent()) {
246  return $acc();
247  }
248  }
249 
255  public function getFunctions()
256  {
257  return $this->_functions;
258  }
259 
267  public function getFunction($name)
268  {
269  if (false === $this->_functions->offsetExists($name)) {
271  'Function %s does not exist.',
272  0,
273  $name
274  );
275  }
276 
277  return $this->_functions[$name];
278  }
279 
285  public function getConstants()
286  {
287  return $this->_constants;
288  }
289 
297  public function getConstant($name)
298  {
299  if (false === $this->_constants->offsetExists($name)) {
301  'Constant %s does not exist',
302  1,
303  $name
304  );
305  }
306 
307  return $this->_constants[$name];
308  }
309 
315  protected function initializeFunctions()
316  {
317  static $_functions = null;
318 
319  if (null === $_functions) {
320  $average = function () {
321  $arguments = func_get_args();
322 
323  return array_sum($arguments) / count($arguments);
324  };
325 
326  $_functions = new \ArrayObject([
327  'abs' => xcallable('abs'),
328  'acos' => xcallable('acos'),
329  'asin' => xcallable('asin'),
330  'atan' => xcallable('atan'),
331  'average' => xcallable($average),
332  'avg' => xcallable($average),
333  'ceil' => xcallable('ceil'),
334  'cos' => xcallable('cos'),
335  'count' => xcallable(function () { return count(func_get_args()); }),
336  'deg2rad' => xcallable('deg2rad'),
337  'exp' => xcallable('exp'),
338  'floor' => xcallable('floor'),
339  'ln' => xcallable('log'),
340  'log' => xcallable(function ($value, $base = 10) { return log($value, $base); }),
341  'max' => xcallable('max'),
342  'min' => xcallable('min'),
343  'pow' => xcallable('pow'),
344  'rad2deg' => xcallable('rad2deg'),
345  'sin' => xcallable('sin'),
346  'sqrt' => xcallable('sqrt'),
347  'sum' => xcallable(function () { return array_sum(func_get_args()); }),
348  'tan' => xcallable('tan')
349  ]);
350  }
351 
352  $this->_functions = $_functions;
353 
354  return;
355  }
356 
362  protected function initializeConstants()
363  {
364  static $_constants = null;
365 
366  if (null === $_constants) {
367  $_constants = new \ArrayObject([
368  'PI' => M_PI,
369  'PI_2' => M_PI_2,
370  'PI_4' => M_PI_4,
371  'E' => M_E,
372  'SQRT_PI' => M_SQRTPI,
373  'SQRT_2' => M_SQRT2,
374  'SQRT_3' => M_SQRT3,
375  'LN_PI' => M_LNPI
376  ]);
377  }
378 
379  $this->_constants = $_constants;
380 
381  return;
382  }
383 
391  public function addFunction($name, $callable = null)
392  {
393  if (null === $callable) {
394  if (false === function_exists($name)) {
395  throw new Math\UnknownFunction(
396  'Function %s does not exist, cannot add it.',
397  2,
398  $name
399  );
400  }
401 
402  $callable = $name;
403  }
404 
405  $this->_functions[$name] = xcallable($callable);
406 
407  return;
408  }
409 
417  public function addConstant($name, $value)
418  {
419  $this->_constants[$name] = $value;
420 
421  return;
422  }
423 }
addFunction($name, $callable=null)
Definition: Arithmetic.php:391
visit(Visitor\Element $element, &$handle=null, $eldnah=null)
Definition: Arithmetic.php:90
addConstant($name, $value)
Definition: Arithmetic.php:417