1<?php
2/* Copyright (c) 1998-2017 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once 'Services/Math/interfaces/interface.ilMathAdapter.php';
5
6/**
7 * Class ilMathBaseAdapter
8 * @author Michael Jansen <mjansen@databay.de>
9 */
10abstract class ilMathBaseAdapter implements ilMathAdapter
11{
12    /**
13     * This method adapts the behaviour of bcscale()
14     * @param mixed   $number
15     * @param integer $scale
16     * @return mixed
17     */
18    public function applyScale($number, $scale = null)
19    {
20        if (is_numeric($number)) {
21            $scale = (int) $scale;
22
23            $number = $this->exp2dec($number);
24            if (strpos($number, '.') === false) {
25                $number_of_decimals = 0;
26            } else {
27                $number_of_decimals = strlen(substr($number, strpos($number, '.') + 1));
28            }
29
30            if ($number_of_decimals > 0 && $number_of_decimals < $scale) {
31                $number = str_pad($number, $scale - $number_of_decimals, '0');
32            } elseif ($number_of_decimals > $scale) {
33                $number = substr($number, 0, -($number_of_decimals - $scale));
34            }
35        }
36
37        return $number;
38    }
39
40    /**
41     * @inheritdoc
42     */
43    public function round($value, $precision = 0)
44    {
45        return number_format($value, $precision, '.', '');
46    }
47
48    /**
49     * {@inheritdoc}
50     */
51    public function equals($left_operand, $right_operand, $scale = null)
52    {
53        return $this->comp($left_operand, $right_operand, $scale) === 0;
54    }
55
56    /**
57     * This function fixes problems which occur when locale ist set to de_DE for example,
58     * because bc* function expecting strings
59     * @param mixed $number
60     * @return string
61     */
62    protected function normalize($number)
63    {
64        if (null === $number) {
65            return $number;
66        }
67
68        $number = str_replace(' ', '', $number);
69        $number = $this->exp2dec($number);
70        $locale_info = localeconv();
71
72        if ($locale_info['decimal_point'] != '.') {
73            $append = '';
74            $number_of_decimals = (int) ini_get('precision') - (int) floor(log10(abs($number)));
75            if (0 > $number_of_decimals) {
76                $number *= pow(10, $number_of_decimals);
77                $append = str_repeat('0', -$number_of_decimals);
78                $number_of_decimals = 0;
79            }
80
81            return number_format($number, $number_of_decimals, '.', '') . $append;
82        }
83
84        return $number;
85    }
86
87    /**
88     * Moved from ilMath...
89     * Converts numbers in the form "1.5e4" into decimal notation
90     * @author Helmut Schottmüller <helmut.schottmueller@mac.com>
91     */
92    protected function exp2dec($float_str)
93    {
94        // make sure its a standard php float string (i.e. change 0.2e+2 to 20)
95        // php will automatically format floats decimally if they are within a certain range
96        $original = $float_str; // store original float, so we can return a float keeping the pecision when possible
97        $float_str = (string) ((float) ($float_str));
98        $float_str = str_replace(",", ".", $float_str); // convert ',' to '.' (float casting was locale sensitive)
99
100        // if there is an E in the float string
101        if (($pos = strpos(strtolower($float_str), 'e')) !== false) {
102            // get either side of the E, e.g. 1.6E+6 => exp E+6, num 1.6
103            $exp = substr($float_str, $pos + 1);
104            $num = substr($float_str, 0, $pos);
105
106            // strip off num sign, if there is one, and leave it off if its + (not required)
107            if ((($num_sign = $num[0]) === '+') || ($num_sign === '-')) {
108                $num = substr($num, 1);
109            } else {
110                $num_sign = '';
111            }
112            if ($num_sign === '+') {
113                $num_sign = '';
114            }
115
116            // strip off exponential sign ('+' or '-' as in 'E+6') if there is one, otherwise throw error, e.g. E+6 => '+'
117            if ((($exp_sign = $exp[0]) === '+') || ($exp_sign === '-')) {
118                $exp = substr($exp, 1);
119            } else {
120                trigger_error("Could not convert exponential notation to decimal notation: invalid float string '$float_str'", E_USER_ERROR);
121            }
122
123            // get the number of decimal places to the right of the decimal point (or 0 if there is no dec point), e.g., 1.6 => 1
124            $right_dec_places = (($dec_pos = strpos($num, '.')) === false) ? 0 : strlen(substr($num, $dec_pos + 1));
125            // get the number of decimal places to the left of the decimal point (or the length of the entire num if there is no dec point), e.g. 1.6 => 1
126            $left_dec_places = ($dec_pos === false) ? strlen($num) : strlen(substr($num, 0, $dec_pos));
127
128            // work out number of zeros from exp, exp sign and dec places, e.g. exp 6, exp sign +, dec places 1 => num zeros 5
129            if ($exp_sign === '+') {
130                $num_zeros = $exp - $right_dec_places;
131            } else {
132                $num_zeros = $exp - $left_dec_places;
133            }
134
135            // build a string with $num_zeros zeros, e.g. '0' 5 times => '00000'
136            $zeros = str_pad('', $num_zeros, '0');
137
138            // strip decimal from num, e.g. 1.6 => 16
139            if ($dec_pos !== false) {
140                $num = str_replace('.', '', $num);
141            }
142
143            // if positive exponent, return like 1600000
144            if ($exp_sign === '+') {
145                return $num_sign . $num . $zeros;
146            }
147            // if negative exponent, return like 0.0000016
148            else {
149                return $num_sign . '0.' . $zeros . $num;
150            }
151        }
152        // otherwise, assume already in decimal notation and return
153        else {
154            return $original;
155        }
156    }
157}
158