Merge pull request #539 from MrPetovan/bug/4555-remove-active-users-feature
[friendica-addons.git] / convert / UnitConvertor.php
1 <?php\r
2 // +----------------------------------------------------------------------+\r
3 // | PHP version 4.0                                                      |\r
4 // +----------------------------------------------------------------------+\r
5 // | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |\r
6 // +----------------------------------------------------------------------+\r
7 // | This source file is subject to version 2.0 of the PHP license,       |\r
8 // | that is bundled with this package in the file LICENSE, and is        |\r
9 // | available at through the world-wide-web at                           |\r
10 // | http://www.php.net/license/2_02.txt.                                 |\r
11 // | If you did not receive a copy of the PHP license and are unable to   |\r
12 // | obtain it through the world-wide-web, please send a note to          |\r
13 // | license@php.net so we can mail you a copy immediately.               |\r
14 // +----------------------------------------------------------------------+\r
15 // | Authors: Stanislav Okhvat <stanis@ngs.ru>                            |\r
16 // | Co-authored by : CVH, Chris Hansel <chris@cpi-service.com>                           |\r
17 // +----------------------------------------------------------------------+\r
18 //\r
19 // $Id: UnitConvertor.php,v 1.00 2002/02/20 11:40:00 stasokhvat Exp $\r
20 \r
21 /**\r
22 * UnitConvertor is able to convert between different units and currencies.\r
23 *\r
24 * @author   Stanislav Okhvat <stanis@sibfair.nsk.su, stanis@ngs.ru>\r
25 * @version  $Id: UnitConvertor.php,v 1.00 2002/03/01 17:00:00 stasokhvat Exp $\r
26 * @package  UnitConvertor\r
27 * @access   public\r
28 * @history  01.03.2002  Implemented the code for regular and offset-based\r
29 *           conversions\r
30 *\r
31 *           13.12.2004\r
32 *           By Chris Hansel (CVH): changed getConvSpecs in order to have it look up \r
33 *           intermediary conversions (also see comments in check_key).\r
34 *\r
35 *           Intermediary conversions are useful when no conversion ratio is specified\r
36 *           between two units when we calculate between the two. For example, we want\r
37 *           to convert between Fahrenheit and Kelvin, and we have only\r
38 *           specified how to convert Centigrade<->Fahrenheit and\r
39 *           Centigrade<->Kelvin. While a direct (Fahrenheit->Kelvin) or\r
40 *           reverse (Kelvin->Fahrenheit) lookups fail, looking for an intermediary\r
41 *           unit linking the two (Centigrade) helps us do the conversion.\r
42 *\r
43 *           13.12.2004\r
44 *           Chris Hansel (CVH): $to_array argument of addConversion method can now\r
45 *           contain units as 'unit1/unit2/unit3', when all units stand for the same\r
46 *           thing. See examples in unitconv.php\r
47 */\r
48 class UnitConvertor\r
49 {\r
50     /**\r
51     * Stores conversion ratios.\r
52     *\r
53     * @var      array\r
54     * @access   private\r
55     */\r
56         var $conversion_table = array();\r
57 \r
58     /**\r
59     * Decimal point character (default is "." - American - set in constructor).\r
60     *\r
61     * @var      string\r
62     * @access   private\r
63     */\r
64         var $decimal_point;\r
65 \r
66     /**\r
67     * Thousands separator (default is "," - American - set in constructor).\r
68     *\r
69     * @var      string\r
70     * @access   private\r
71     */\r
72         var $thousand_separator;\r
73 \r
74     /**\r
75     * For future use\r
76     *\r
77     * @var      array\r
78     * @access   private\r
79     */\r
80         var $bases = array();\r
81 \r
82     /**\r
83     * Constructor. Initializes the UnitConvertor object with the most important\r
84         * properties.\r
85     *\r
86     * @param    string    decimal point character\r
87         * @param    string    thousand separator character\r
88     * @return   void\r
89     * @access   public\r
90     */\r
91         function UnitConvertor($dec_point = '.', $thousand_sep = ',')\r
92         {\r
93                 $this->decimal_point = $dec_point;\r
94                 $this->thousand_separator = $thousand_sep;\r
95 \r
96         } // end func UnitConvertor\r
97 \r
98     /**\r
99     * Adds a conversion ratio to the conversion table.\r
100     *\r
101     * @param    string    the name of unit from which to convert\r
102         * @param    array     array(\r
103     *                                           "pound"=>array("ratio"=>'', "offset"=>'')\r
104     *                                     )\r
105     *                                     "pound" - name of unit to set conversion ration to\r
106     *                                     "ratio" - 'double' conversion ratio which, when\r
107     *                                     multiplied by the number of $from_unit units produces\r
108     *                                     the result\r
109     *                                     "offset" - an offset from 0 which will be added to\r
110     *                                     the result when converting (needed for temperature\r
111     *                                     conversions and defaults to 0).\r
112     * @return   boolean   true if successful, false otherwise\r
113     * @access   public\r
114     */\r
115         function addConversion($from_unit, $to_array)\r
116         {\r
117                 if (!isset($this->conversion_table[$from_unit])) {\r
118                         while(list($key, $val) = each($to_array))\r
119                         {\r
120                                 if (strstr($key, '/'))\r
121                                 {\r
122                                         $to_units = explode('/', $key);\r
123                                         foreach ($to_units as $to_unit)\r
124                                         {\r
125                                                 $this->bases[$from_unit][] = $to_unit;\r
126 \r
127                                                 if (!is_array($val))\r
128                                                 {\r
129                                                         $this->conversion_table[$from_unit."_".$to_unit] = array("ratio"=>$val, "offset"=>0);\r
130                                                 }\r
131                                                 else\r
132                                                 {\r
133                                                         $this->conversion_table[$from_unit."_".$to_unit] =\r
134                                                                 array(\r
135                                                                         "ratio"=>$val['ratio'],\r
136                                                                         "offset"=>(isset($val['offset']) ? $val['offset'] : 0)\r
137                                                                 );\r
138                                                 }\r
139                                         }\r
140                                 }\r
141                                 else\r
142                                 {\r
143                                         $this->bases[$from_unit][] = $key;\r
144 \r
145                                         if (!is_array($val))\r
146                                         {\r
147                                                 $this->conversion_table[$from_unit."_".$key] = array("ratio"=>$val, "offset"=>0);\r
148                                         }\r
149                                         else\r
150                                         {\r
151                                                 $this->conversion_table[$from_unit."_".$key] =\r
152                                                 array(\r
153                                                         "ratio"=>$val['ratio'],\r
154                                                         "offset"=>(isset($val['offset']) ? $val['offset'] : 0)\r
155                                                 );\r
156                                         }\r
157                                 }\r
158                         }\r
159                         return true;\r
160                 }\r
161                 return false;\r
162 \r
163         } // end func addConversion\r
164 \r
165     /**\r
166     * Converts from one unit to another using specified precision.\r
167     *\r
168     * @param    double    value to convert\r
169         * @param    string    name of the source unit from which to convert\r
170     * @param    string    name of the target unit to which we are converting\r
171     * @param    integer   double precision of the end result\r
172     * @return   void\r
173     * @access   public\r
174     */\r
175         function convert($value, $from_unit, $to_unit, $precision)\r
176         {\r
177                 if ($this->getConvSpecs($from_unit, $to_unit, $value, $converted ))\r
178                 {\r
179                         return number_format($converted , (int)$precision, $this->decimal_point, $this->thousand_separator);\r
180                 } else {\r
181                         return false;\r
182                 }\r
183         } // end func\r
184 \r
185         /**\r
186         * CVH : changed this Function getConvSpecs in order to have it look up \r
187         * intermediary Conversions from the \r
188         * "base" unit being that one that has the highest hierarchical order in one\r
189         * "logical" Conversion_Array\r
190         * when taking $conv->addConversion('km',\r
191         * array('meter'=>1000, 'dmeter'=>10000, 'centimeter'=>100000,\r
192         * 'millimeter'=>1000000, 'mile'=>0.62137, 'naut.mile'=>0.53996,\r
193         * 'inch(es)/zoll'=>39370, 'ft/foot/feet'=>3280.8, 'yd/yard'=>1093.6));\r
194         * "km" would be the logical base unit for all units of dinstance, thus, \r
195         * if the function fails to find a direct or reverse conversion in the table \r
196         * it is only logical to suspect that if there is a chance \r
197         * converting the value it only is via the "base" unit, and so \r
198         * there is not even a need for a recursive search keeping the perfomance \r
199         * acceptable and the ressource small...\r
200         *\r
201         * CVH check_key checks for a key in the Conversiontable and returns a value\r
202         */\r
203         function  check_key( $key) {\r
204                 if ( array_key_exists ($key,$this->conversion_table)) {\r
205                         if (! empty($this->conversion_table[$key])) {\r
206                                 return $this->conversion_table[$key];\r
207                         }\r
208                 }\r
209                 return false;\r
210         }\r
211 \r
212         /**\r
213     * Key function. Finds the conversion ratio and offset from one unit to another.\r
214     *\r
215         * @param    string    name of the source unit from which to convert\r
216     * @param    string    name of the target unit to which we are converting\r
217     * @param    double    conversion ratio found. Returned by reference.\r
218     * @param    double    offset which needs to be added (or subtracted, if negative)\r
219         *                     to the result to convert correctly. \r
220         *                     For temperature or some scientific conversions,\r
221     *                                     i.e. Fahrenheit -> Celcius\r
222     * @return   boolean   true if ratio and offset are found for the supplied\r
223     *                                     units, false otherwise\r
224     * @access   private\r
225     */\r
226         function getConvSpecs($from_unit, $to_unit, $value, &$converted)\r
227         {\r
228                 $key = $from_unit."_".$to_unit;\r
229                 $revkey = $to_unit."_".$from_unit;\r
230                 $found = false;\r
231                 if ($ct_arr = $this->check_key($key)) {\r
232                         // Conversion Specs found directly\r
233                         $ratio = (double)$ct_arr['ratio'];\r
234                         $offset = $ct_arr['offset']; \r
235                         $converted = (double)(($value  * $ratio)+ $offset);\r
236                         \r
237                         return true;\r
238                 }       // not found in direct order, try reverse order\r
239                 elseif ($ct_arr = $this->check_key($revkey)) {\r
240                         $ratio = (double)(1/$ct_arr['ratio']);\r
241                         $offset = -$ct_arr['offset'];\r
242                         $converted = (double)(($value  + $offset) *  $ratio);\r
243                         \r
244                         return true;\r
245                 }       // not found test for intermediary conversion\r
246                 else {\r
247                         // return ratio = 1 if keyparts match\r
248                         if ($key == $revkey) {\r
249                                             $ratio = 1;\r
250                                                 $offset = 0;\r
251                                                 $converted = $value;\r
252                                                 return true;\r
253                         }               \r
254                         // otherwise search intermediary\r
255                         reset($this->conversion_table);\r
256                         while (list($convk, $i1_value) = each($this->conversion_table)) {\r
257                                 // split the key into parts\r
258                                 $keyparts = preg_split("/_/",$convk);\r
259                                 // return ratio = 1 if keyparts match\r
260                         \r
261                                 // Now test if either part matches the from or to unit\r
262                                 if ($keyparts[1] == $to_unit && ($i2_value = $this->check_key($keyparts[0]."_".$from_unit))) {\r
263                                                 // an intermediary $keyparts[0] was found\r
264                                                 // now let us put things together intermediary 1 and 2\r
265                                                 $converted = (double)(((($value - $i2_value['offset']) / $i2_value['ratio']) * $i1_value['ratio'])+ $i1_value['offset']);\r
266 \r
267                                                 $found = true;\r
268                                                 \r
269                                 } elseif ($keyparts[1] == $from_unit && ($i2_value = $this->check_key($keyparts[0]."_".$to_unit))) {\r
270                                                 // an intermediary $keyparts[0] was found\r
271                                                 // now let us put things together intermediary 2 and 1\r
272                                                 $converted = (double)(((($value - $i1_value['offset']) / $i1_value['ratio']) + $i2_value['offset']) * $i2_value['ratio']);\r
273 \r
274                                                 $found = true;                  \r
275                                 } \r
276                         }\r
277                         return $found;          \r
278                 }\r
279 \r
280         } // end func getConvSpecs\r
281         \r
282 } // end class UnitConvertor\r
283 ?>