Open Journal Systems  3.3.0
NumberHelper.php
1 <?php
2 /*
3  * citeproc-php
4  *
5  * @link http://github.com/seboettg/citeproc-php for the source repository
6  * @copyright Copyright (c) 2016 Sebastian Böttger.
7  * @license https://opensource.org/licenses/MIT
8  */
9 
10 namespace Seboettg\CiteProc\Util;
11 
12 use Closure;
13 
21 {
22 
23  const PATTERN_ORDINAL = "/\d+(st|nd|rd|th)?\.?$/";
24 
25  const PATTERN_ROMAN = "/^[ivxlcdm]+\.?$/i";
26 
27  const PATTERN_ROMAN_RANGE = "/^([ivxlcdm]+\.*\s*[*\â“\-&+,;]\s*){1,}[ivxlcdm]+\.?$/i"; const PATTERN_AFFIXES = "/^[a-z]?\d+[a-z]?$/i"; const PATTERN_COMMA_AMPERSAND_RANGE = "/\d*([\s?\-&+,;\s])+\d+/"; const ROMAN_NUMERALS = [ ["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"], ["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"], ["", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"], ["", "m", "mm", "mmm", "mmmm", "mmmmm"] ]; const ROMAN_DIGITS = [ "M" => 1000, "D" => 500, "C" => 100, "L" => 50, "X" => 10, "V" => 5, "I" => 1 ]; /** * @return Closure */ public static function getCompareNumber() { return function ($numA, $numB, $order) { if (is_numeric($numA) && is_numeric($numB)) { $ret = $numA - $numB; } else { $ret = strcasecmp($numA, $numB); } if ("descending" === $order) { return $ret > 0 ? -1 : 1; } return $ret > 0 ? 1 : -1; }; } /** * @param $num * @return string */ public static function dec2roman($num) { $ret = ""; if ($num < 6000) { $numStr = strrev($num); $len = strlen($numStr); for ($pos = 0; $pos < $len; $pos++) { $n = $numStr[$pos]; $ret = self::ROMAN_NUMERALS[$pos][$n] . $ret; } } return $ret; } /** * @param $romanNumber * @return int|mixed */ public static function roman2Dec($romanNumber) { $romanNumber = trim($romanNumber); if (is_numeric($romanNumber)) { return 0; } $values = []; // Convert the string to an array of roman values: for ($i = 0; $i < strlen($romanNumber); ++$i) { $char = strtoupper($romanNumber[$i]); if (self::ROMAN_DIGITS[$char] !== null) { $values[] = self::ROMAN_DIGITS[$char]; } } $sum = 0; while ($current = current($values)) { $next = next($values); $next > $current ? $sum += $next - $current + 0 * next($values) : $sum += $current; } return $sum; } /** * @param $str * @return bool */ public static function isRomanNumber($str) { $number = trim($str); for ($i = 0; $i < strlen($number); ++$i) { $char = strtoupper($number[$i]); if (!in_array($char, array_keys(self::ROMAN_DIGITS))) { return false; } } return true; } /** * @param $str * @return string */ public static function evaluateStringPluralism($str) { $plural = 'single'; if (!empty($str)) { $isRange = self::isRange($str); if ($isRange) { return 'multiple'; } else { if (is_numeric($str) || NumberHelper::isRomanNumber($str)) { return 'single'; } } } return $plural; } /** * @param $string * @return mixed */ public static function extractNumber($string) { if (preg_match("/(\d+)[^\d]*$/", $string, $match)) { return $match[1]; } return $string; } /** * @param $str * @return array[]|false|string[] */ public static function splitByRangeDelimiter($str) { return preg_split("/[-–&,]/", $str); } /** * @param string $str * @return bool */ private static function isRange($str) { $rangeParts = self::splitByRangeDelimiter($str); $isRange = false; if (count($rangeParts) > 1) { $isRange = true; foreach ($rangeParts as $range) { if (NumberHelper::isRomanNumber(trim($range)) || is_numeric(trim($range))) { $isRange = $isRange && true; } } } return $isRange; } /** * @param int|string $number * @return bool */ public static function isRomanRange($number) { return preg_match(self::PATTERN_ROMAN_RANGE, $number); } } €“\-&+,;]\s*){1,}[ivxlcdm]+\.?$/i";
28 
29  const PATTERN_AFFIXES = "/^[a-z]?\d+[a-z]?$/i";
30 
31  const PATTERN_COMMA_AMPERSAND_RANGE = "/\d*([\s?\-&+,;\s])+\d+/";
32 
33  const ROMAN_NUMERALS = [
34  ["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"],
35  ["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"],
36  ["", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"],
37  ["", "m", "mm", "mmm", "mmmm", "mmmmm"]
38  ];
39 
40  const ROMAN_DIGITS = [
41  "M" => 1000,
42  "D" => 500,
43  "C" => 100,
44  "L" => 50,
45  "X" => 10,
46  "V" => 5,
47  "I" => 1
48  ];
49 
53  public static function getCompareNumber()
54  {
55  return function ($numA, $numB, $order) {
56  if (is_numeric($numA) && is_numeric($numB)) {
57  $ret = $numA - $numB;
58  } else {
59  $ret = strcasecmp($numA, $numB);
60  }
61  if ("descending" === $order) {
62  return $ret > 0 ? -1 : 1;
63  }
64  return $ret > 0 ? 1 : -1;
65  };
66  }
67 
72  public static function dec2roman($num)
73  {
74  $ret = "";
75  if ($num < 6000) {
76  $numStr = strrev($num);
77  $len = strlen($numStr);
78  for ($pos = 0; $pos < $len; $pos++) {
79  $n = $numStr[$pos];
80  $ret = self::ROMAN_NUMERALS[$pos][$n] . $ret;
81  }
82  }
83  return $ret;
84  }
85 
90  public static function roman2Dec($romanNumber)
91  {
92  $romanNumber = trim($romanNumber);
93  if (is_numeric($romanNumber)) {
94  return 0;
95  }
96 
97  $values = [];
98  // Convert the string to an array of roman values:
99  for ($i = 0; $i < strlen($romanNumber); ++$i) {
100  $char = strtoupper($romanNumber[$i]);
101  if (self::ROMAN_DIGITS[$char] !== null) {
102  $values[] = self::ROMAN_DIGITS[$char];
103  }
104  }
105 
106  $sum = 0;
107  while ($current = current($values)) {
108  $next = next($values);
109  $next > $current ? $sum += $next - $current + 0 * next($values) : $sum += $current;
110  }
111  return $sum;
112  }
113 
118  public static function isRomanNumber($str)
119  {
120  $number = trim($str);
121  for ($i = 0; $i < strlen($number); ++$i) {
122  $char = strtoupper($number[$i]);
123  if (!in_array($char, array_keys(self::ROMAN_DIGITS))) {
124  return false;
125  }
126  }
127  return true;
128  }
129 
134  public static function evaluateStringPluralism($str)
135  {
136  $plural = 'single';
137  if (!empty($str)) {
138  $isRange = self::isRange($str);
139  if ($isRange) {
140  return 'multiple';
141  } else {
142  if (is_numeric($str) || NumberHelper::isRomanNumber($str)) {
143  return 'single';
144  }
145  }
146  }
147  return $plural;
148  }
149 
154  public static function extractNumber($string)
155  {
156  if (preg_match("/(\d+)[^\d]*$/", $string, $match)) {
157  return $match[1];
158  }
159  return $string;
160  }
161 
166  public static function splitByRangeDelimiter($str)
167  {
168  return preg_split("/[-–&,]/", $str);
169  }
170 
175  private static function isRange($str)
176  {
177  $rangeParts = self::splitByRangeDelimiter($str);
178  $isRange = false;
179  if (count($rangeParts) > 1) {
180  $isRange = true;
181  foreach ($rangeParts as $range) {
182  if (NumberHelper::isRomanNumber(trim($range)) || is_numeric(trim($range))) {
183  $isRange = $isRange && true;
184  }
185  }
186  }
187  return $isRange;
188  }
189 
194  public static function isRomanRange($number)
195  {
196  return preg_match(self::PATTERN_ROMAN_RANGE, $number);
197  }
198 }
Seboettg\CiteProc\Util\NumberHelper\PATTERN_COMMA_AMPERSAND_RANGE
const PATTERN_COMMA_AMPERSAND_RANGE
Definition: NumberHelper.php:31
Seboettg\CiteProc\Util\NumberHelper\evaluateStringPluralism
static evaluateStringPluralism($str)
Definition: NumberHelper.php:134
Seboettg\CiteProc\Util\NumberHelper\dec2roman
static dec2roman($num)
Definition: NumberHelper.php:72
Seboettg\CiteProc\Util\NumberHelper\isRomanNumber
static isRomanNumber($str)
Definition: NumberHelper.php:118
Seboettg\CiteProc\Util\NumberHelper\ROMAN_DIGITS
const ROMAN_DIGITS
Definition: NumberHelper.php:40
Seboettg\CiteProc\Util\NumberHelper
Definition: NumberHelper.php:20
Seboettg\CiteProc\Util\NumberHelper\extractNumber
static extractNumber($string)
Definition: NumberHelper.php:154
Seboettg\CiteProc\Util\NumberHelper\roman2Dec
static roman2Dec($romanNumber)
Definition: NumberHelper.php:90
Seboettg\CiteProc\Util\NumberHelper\PATTERN_ORDINAL
const PATTERN_ORDINAL
Definition: NumberHelper.php:23
Seboettg\CiteProc\Util\NumberHelper\isRomanRange
static isRomanRange($number)
Definition: NumberHelper.php:194
Seboettg\CiteProc\Util\NumberHelper\getCompareNumber
static getCompareNumber()
Definition: NumberHelper.php:53
Seboettg\CiteProc\Util\NumberHelper\PATTERN_ROMAN_RANGE
const PATTERN_ROMAN_RANGE
Definition: NumberHelper.php:27
Seboettg\CiteProc\Util\NumberHelper\splitByRangeDelimiter
static splitByRangeDelimiter($str)
Definition: NumberHelper.php:166
Seboettg\CiteProc\Util\NumberHelper\ROMAN_NUMERALS
const ROMAN_NUMERALS
Definition: NumberHelper.php:33
Seboettg\CiteProc\Util\NumberHelper\PATTERN_ROMAN
const PATTERN_ROMAN
Definition: NumberHelper.php:25
Seboettg\CiteProc\Util
Definition: CiteProcHelper.php:10
Seboettg\CiteProc\Util\NumberHelper\PATTERN_AFFIXES
const PATTERN_AFFIXES
Definition: NumberHelper.php:29