1<?php
2/* Copyright (c) 1998-2016 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4/**
5 * Class ilMath
6 * Wrapper for mathematical operations
7 * @author Helmut Schottmüller <helmut.schottmueller@mac.com>
8 * @author Michael Jansen <mjansen@databay.de>
9 * $Id$
10 */
11class ilMath
12{
13    /**
14     * @var ilMathAdapter
15     */
16    protected static $default_adapter = null;
17
18    /**
19     * @param int|float $left_operand
20     * @param int|float $right_operand
21     * @param int $scale
22     * @return mixed
23     */
24    public static function _add($left_operand, $right_operand, $scale = 50)
25    {
26        $adapter = static::getDefaultAdapter();
27
28        return $adapter->add($left_operand, $right_operand, $scale);
29    }
30
31    /**
32     * @param int|float $left_operand
33     * @param int|float $right_operand
34     * @param int $scale
35     * @return mixed
36     */
37    public static function _div($left_operand, $right_operand, $scale = 50)
38    {
39        $adapter = static::getDefaultAdapter();
40
41        return $adapter->div($left_operand, $right_operand, $scale);
42    }
43
44    /**
45     * @param int|float $operand
46     * @param int|float $modulu
47     * @return int
48     */
49    public static function _mod($operand, $modulu)
50    {
51        $adapter = static::getDefaultAdapter();
52
53        return $adapter->mod($operand, $modulu);
54    }
55
56    /**
57     * @param int|float $left_operand
58     * @param int|float $right_operand
59     * @param int $scale
60     * @return mixed
61     */
62    public static function _mul($left_operand, $right_operand, $scale = 50)
63    {
64        $adapter = static::getDefaultAdapter();
65
66        return $adapter->mul($left_operand, $right_operand, $scale);
67    }
68
69    /**
70     * @param int|float $left_operand
71     * @param int|float $right_operand
72     * @param int $scale
73     * @return mixed
74     */
75    public static function _pow($left_operand, $right_operand, $scale = 50)
76    {
77        $adapter = static::getDefaultAdapter();
78
79        return $adapter->pow($left_operand, $right_operand, $scale);
80    }
81
82    /**
83     * @param int|float $operand
84     * @param int $scale
85     * @return mixed
86     */
87    public static function _sqrt($operand, $scale = 50)
88    {
89        $adapter = static::getDefaultAdapter();
90
91        return $adapter->sqrt($operand, $scale);
92    }
93
94    /**
95     * @param int|float $left_operand
96     * @param int|float $right_operand
97     * @param int $scale
98     * @return mixed
99     */
100    public static function _sub($left_operand, $right_operand, $scale = 50)
101    {
102        $adapter = static::getDefaultAdapter();
103
104        return $adapter->sub($left_operand, $right_operand, $scale);
105    }
106
107    public static function isCoprimeFraction($numerator, $denominator)
108    {
109        $gcd = self::getGreatestCommonDivisor(abs($numerator), abs($denominator));
110
111        return $gcd == 1 ? true : false;
112    }
113
114    /**
115     * @param mixed $a
116     * @param mixed $b
117     * @return mixed
118     */
119    public static function getGreatestCommonDivisor($a, $b)
120    {
121        if ($b > 0) {
122            return self::getGreatestCommonDivisor($b, $a % $b);
123        } else {
124            return $a;
125        }
126    }
127
128    /**
129     * @param ilMathAdapter $adapter
130     */
131    public static function setDefaultAdapter(ilMathAdapter $adapter)
132    {
133        static::$default_adapter = $adapter;
134    }
135
136    /**
137     * @return ilMathAdapter
138     */
139    public static function getDefaultAdapter()
140    {
141        if (null === static::$default_adapter) {
142            static::$default_adapter = static::getFirstValidAdapter();
143        }
144
145        return static::$default_adapter;
146    }
147
148    /**
149     * @param null|string $adapter
150     * @return ilMathAdapter
151     * @throws ilMathException
152     */
153    public static function getInstance($adapter = null)
154    {
155        if (null === $adapter) {
156            return static::getFirstValidAdapter();
157        }
158
159        $class_name = 'ilMath' . $adapter . 'Adapter';
160        $path_to_class = realpath('Services/Math/classes/class.' . $class_name . '.php');
161
162        if (!is_file($path_to_class) || !is_readable($path_to_class)) {
163            require_once 'Services/Math/exceptions/class.ilMathException.php';
164            throw new ilMathException(sprintf(
165                'The math adapter %s is not valid, please refer to a class implementing %s',
166                $adapter,
167                ilMathAdapter::class
168            ));
169        }
170
171        require_once $path_to_class;
172        if (!class_exists($class_name) || !is_subclass_of($class_name, ilMathAdapter::class)) {
173            require_once 'Services/Math/exceptions/class.ilMathException.php';
174            throw new ilMathException(sprintf(
175                'The math adapter class %s is not valid, please refer to a class implementing %s',
176                $class_name,
177                ilMathAdapter::class
178            ));
179        }
180
181        return new $class_name();
182    }
183
184    /**
185     * @return ilMathAdapter
186     */
187    public static function getFirstValidAdapter()
188    {
189        if (extension_loaded('bcmath')) {
190            return static::getInstance('BCMath');
191        }
192
193        return static::getInstance('Php');
194    }
195
196    /**
197     * Backward compatibility: Map all static calls to an equivalent instance method of the adapter
198     * @param string $method
199     * @param mixed $args
200     * @return mixed
201     */
202    public static function __callStatic($method, $args)
203    {
204        if (strpos($method, '_') === 0) {
205            $method = substr($method, 1);
206        }
207
208        $adapter = static::getDefaultAdapter();
209
210        return call_user_func_array([$adapter, $method], $args);
211    }
212}
213