/** * Randomizes the positions of the elements in an array. * * @param array $array The array to be shuffled. * * @return void */ public static function shuffle($array) { assert('is_carray($array)', vs(isset($this), get_defined_vars())); $array = splarray($array); // The Fisher-Yates algorithm. for ($i = $array->getSize() - 1; $i > 0; $i--) { $exchangeIdx = CMathi::intervalRandom(0, $i); $save = $array[$exchangeIdx]; $array[$exchangeIdx] = $array[$i]; $array[$i] = $save; } }
/** * Randomizes the positions of the characters in a string and returns the new string. * * @param string $string The string to be shuffled. * * @return string The shuffled string. */ public static function shuffle($string) { assert('is_cstring($string)', vs(isset($this), get_defined_vars())); // The Fisher-Yates algorithm. for ($i = strlen($string) - 1; $i > 0; $i--) { $exchangeIdx = CMathi::intervalRandom(0, $i); $save = $string[$exchangeIdx]; $string[$exchangeIdx] = $string[$i]; $string[$i] = $save; } return $string; }
protected static function shiftTimeInTimeZone(CTime $time, $timeUnit, $quantity, $timeZone) { $units; switch ($timeUnit) { case self::SECOND: $units = "seconds"; break; case self::MINUTE: $units = "minutes"; break; case self::HOUR: $units = "hours"; break; case self::DAY: $units = "days"; break; case self::WEEK: $units = "weeks"; break; case self::MONTH: $units = "months"; break; case self::YEAR: $units = "years"; break; default: assert('false', vs(isset($this), get_defined_vars())); break; } $dt = new DateTime(); $dt->setTimestamp($time->UTime()); $dt->setTimezone(is_cstring($timeZone) ? new DateTimeZone($timeZone) : $timeZone->DTimeZone()); $sign = $quantity < 0 ? "-" : "+"; $absQty = CString::fromInt(CMathi::abs($quantity)); $dt->modify("{$sign}{$absQty} {$units}"); $UTime = $dt->getTimestamp(); $MTime = $time->MTime(); if ($UTime != 0 && $MTime != 0 && CMathi::sign($UTime) != CMathi::sign($MTime)) { if ($UTime < 0) { // $MTime > 0 $UTime++; $MTime -= 1000; } else { // $MTime < 0 $UTime--; $MTime += 1000; } } return new self($UTime, $MTime); }
/** * Wraps the text in a string to a specified width and returns the new string. * * @param string $string The string with the text to be wrapped. * @param int $width The wrapping width, in characters. * @param bitfield $wrappingFlags **OPTIONAL. Default is** `WRAPPING_DEFAULT`. The wrapping option(s). The * available options are `WRAPPING_BREAK_SPACELESS_LINES`, `WRAPPING_ALLOW_TRAILING_SPACES`, * `WRAPPING_DISALLOW_LEADING_SPACES`, and `WRAPPING_DONT_BREAK_SPACELESS_CJK_ENDING_LINES` * (see [Summary](#summary)). * @param string $newline **OPTIONAL. Default is** LF (U+000A). The newline character(s) to be used for making * new lines in the process of wrapping. * * @return string The wrapped text. */ public static function wordWrap($string, $width, $wrappingFlags = self::WRAPPING_DEFAULT, $newline = self::NEWLINE) { assert('is_cstring($string) && is_int($width) && is_bitfield($wrappingFlags) && is_cstring($newline)', vs(isset($this), get_defined_vars())); assert('$width > 0', vs(isset($this), get_defined_vars())); // Constant. Newline character that is used by the input string (after newline normalization). $normNl = self::NEWLINE; // Constant. Determines what characters should be considered spaces. // A character in the "Zs" Unicode category, an HT, or a Zero Width Space, except No-break Space and Narrow // No-break Space. $spaceSubjectRe = "(\\p{Zs}|\\x{0009}|\\x{200B})(?<!\\x{00A0}|\\x{202F})"; // Break enabling characters. // Soft Hyphen or Tibetan Mark Intersyllabic Tsheg. $breakAllowCharSubjectRe = "\\x{00AD}|\\x{0F0B}"; // Retrieve the wrapping options. $breakSpacelessLines = CBitField::isBitSet($wrappingFlags, self::WRAPPING_BREAK_SPACELESS_LINES); $allowTrailingSpaces = CBitField::isBitSet($wrappingFlags, self::WRAPPING_ALLOW_TRAILING_SPACES); $disallowLeadingSpaces = CBitField::isBitSet($wrappingFlags, self::WRAPPING_DISALLOW_LEADING_SPACES); $dontBreakSpacelessCjkEndingLines = CBitField::isBitSet($wrappingFlags, self::WRAPPING_DONT_BREAK_SPACELESS_CJK_ENDING_LINES); // Normalize newlines in the input string. $string = self::normNewlines($string, $normNl); $normNlLength = self::length($normNl); $newString = ""; $pos = 0; $bytePos = 0; $sLength = self::length($string); while (true) { $numCharsLeft = $sLength - $pos; // A portion begins at the very start or right after a newline, either it is native or added. The length of // a portion is the wrapping width or less. $portionLength = CMathi::min($width, $numCharsLeft); $portion = self::substr($string, $pos, $portionLength); $portionByteLength = CString::length($portion); if ($portionLength == $numCharsLeft) { // All done. $newString .= $portion; break; } // The starting position of the next portion. $nextPos = $pos + $portionLength; $nextBytePos = $bytePos + $portionByteLength; // Look for the first occurrence of a newline in the portion. $nlPos = self::indexOf($portion, $normNl); if ($nlPos != -1) { // This portion contains a newline, so the next portion is going to start right after this first found // newline. $subPLength = $nlPos + $normNlLength; $subP = self::substr($portion, 0, $subPLength); $newString .= $subP; $pos += $subPLength; $bytePos += CString::length($subP); continue; } // There are no newlines in this portion. Before the next step, make sure that the next portion is not // going to start with a newline. if ($numCharsLeft - $portionLength >= $normNlLength) { $nextPortionBeginning = self::substr($string, $nextPos, $normNlLength); if (self::indexOf($nextPortionBeginning, $normNl) == 0) { // The next portion is going to start with a newline, so no need to break this one, regardless of // whether or not it contains any spaces. $newString .= $portion; $pos = $nextPos; $bytePos = $nextBytePos; continue; } } // The next portion is not going to start with a newline. Look for the last occurrence of a space or // break-allow character in this portion. $lastSubjectBytePos = CRegex::lastIndexOf($portion, "/({$spaceSubjectRe})|({$breakAllowCharSubjectRe})/u", 0, $foundString); if ($lastSubjectBytePos != -1) { // Add a newline right after this last occurring space or break-allow character. $subP = CString::substring($portion, 0, $lastSubjectBytePos + CString::length($foundString)); $newString .= $subP; $newString .= $newline; $pos += self::length($subP); $bytePos += CString::length($subP); continue; } // There are no spaces or break-allow characters in this portion. Consider adding a newline right after the // portion. if ($breakSpacelessLines || !$dontBreakSpacelessCjkEndingLines && self::hasCjkChar(self::charAt($portion, $portionLength - 1))) { $newString .= $portion; $newString .= $newline; $pos = $nextPos; $bytePos = $nextBytePos; continue; } // There are no spaces or break-allow characters in this portion and it should go adjacent to the upcoming // text. Look for the first newline, space, or break-allow character in the upcoming text. $nextSubjectBytePos = CRegex::indexOf($string, "/{$normNl}|(({$spaceSubjectRe})|({$breakAllowCharSubjectRe}))(?!{$normNl})/u", $nextBytePos, $foundString); if ($nextSubjectBytePos != -1) { // Found a newline, space, or a break-allow character, so the next portion is going to start right // after it. $afterP = CString::substring($string, $nextBytePos, $nextSubjectBytePos + CString::length($foundString)); $newString .= $portion; $newString .= $afterP; if (!CString::equals($foundString, $normNl)) { // It is a space or break-allow character that was found, so add a newline after it. $newString .= $newline; } $pos += $portionLength + self::length($afterP); $bytePos += $portionByteLength + CString::length($afterP); continue; } // There are no spaces, newlines, or break-allow characters in the upcoming text. Finalize according to the // breaking options. if (!$breakSpacelessLines) { $newString .= $portion; $newString .= self::substr($string, $nextPos); } else { $newString .= $portion; $newString .= $newline; $pos = $nextPos; while (true) { $numCharsLeft = $sLength - $pos; $portionLength = CMathi::min($width, $numCharsLeft); $newString .= self::substr($string, $pos, $portionLength); if ($portionLength == $numCharsLeft) { break; } $newString .= $newline; $pos += $portionLength; } } break; } if (!$allowTrailingSpaces) { // Remove trailing spaces. $newString = CRegex::remove($newString, "/({$spaceSubjectRe})+(?={$normNl}|\\z)/u"); } if ($disallowLeadingSpaces) { // Remove leading spaces. $newString = CRegex::remove($newString, "/(?<={$normNl}|^)({$spaceSubjectRe})+/u"); } return $newString; }
/** * @ignore */ public static function onThirdPartyUpdateByPackageManager() { if (!self::isInCliMode()) { // This method can be run in CLI mode only. assert('false', vs(isset($this), get_defined_vars())); } $timeoutPause = new CTimeoutPause(); CShell::speak("Processing third-party components ..."); $tpDps = CFile::listDirectories(CFilePath::absolute($GLOBALS["PHRED_PATH_TO_THIRD_PARTY"])); $ignorePackages = CConfiguration::option("upd.thirdPartyOopWrappingIgnorePackages"); $ignorePackagesL2 = CArray::filter($ignorePackages, function ($package) { return CString::find($package, "/"); }); $newTpDps = CArray::make(); $len = CArray::length($tpDps); for ($i = 0; $i < $len; $i++) { $tpDp = $tpDps[$i]; $dirName = CFilePath::name($tpDp); if (!CArray::find($ignorePackages, $dirName)) { $dpHasL2DirsToIgnore = CArray::find($ignorePackagesL2, $dirName, function ($packageL2, $dirName) { return CString::equals(CFilePath::directory($packageL2), $dirName); }); if (!$dpHasL2DirsToIgnore) { CArray::push($newTpDps, $tpDp); } else { $tpSubDps = CFile::listDirectories($tpDp); $tpSubDps = CArray::filter($tpSubDps, function ($subDp) use($ignorePackagesL2) { return !CArray::find($ignorePackagesL2, $subDp, function ($packageL2, $subDp) { return CString::endsWith($subDp, $packageL2); }); }); CArray::pushArray($newTpDps, $tpSubDps); } } } $tpDps = $newTpDps; $wrapProtectedMethods = CConfiguration::option("upd.thirdPartyOopWrappingInProtectedMethods"); $wrapPrivateMethods = CConfiguration::option("upd.thirdPartyOopWrappingInPrivateMethods"); static $s_stdPhpTag = "<?php"; static $s_progressResolution = 0.05; $prevProgressDivR = 0; $tpDpsLen = CArray::length($tpDps); for ($i0 = 0; $i0 < $tpDpsLen; $i0++) { $tpFps = CFile::reFindFilesRecursive($tpDps[$i0], "/\\.php\\d?\\z/"); $tpFpsLen = CArray::length($tpFps); for ($i1 = 0; $i1 < $tpFpsLen; $i1++) { $fileCode = CFile::read($tpFps[$i1]); if (!CString::find($fileCode, self::$ms_thirdPartyAlreadyOopWrappedMark)) { $parser = new PhpParser\Parser(new PhpParser\Lexer()); try { // Parse the code. $statements = $parser->parse($fileCode); // Wrap the code into OOP. $traverser = new PhpParser\NodeTraverser(); $mainVisitor = new CMainVisitor($wrapProtectedMethods, $wrapPrivateMethods); $traverser->addVisitor($mainVisitor); $statements = $traverser->traverse($statements); $wrappedCode = (new PhpParser\PrettyPrinter\Standard())->prettyPrint($statements); $phpTagPos = CString::indexOf($wrappedCode, $s_stdPhpTag); if ($phpTagPos == -1) { $wrappedCode = "{$s_stdPhpTag}\n\n{$wrappedCode}"; $phpTagPos = 0; } $wrappedCode = CString::insert($wrappedCode, $phpTagPos + CString::length($s_stdPhpTag), "\n\n" . self::$ms_thirdPartyAlreadyOopWrappedMark); // Save. CFile::write($tpFps[$i1], $wrappedCode); } catch (PhpParser\Error $parserError) { CShell::say("\nPhpParser: " . $tpFps[$i1] . ", at line " . $parserError->getRawLine() . ": " . $parserError->getRawMessage()); } } $progress = (double) ($i0 / $tpDpsLen + 1 / $tpDpsLen * $i1 / $tpFpsLen); $progressDivR = CMathi::floor($progress / $s_progressResolution); if ($progressDivR != $prevProgressDivR) { $perc = CMathi::round($progressDivR * $s_progressResolution * 100); CShell::speak("{$perc}%"); } $prevProgressDivR = $progressDivR; } } CShell::speak("100%"); CShell::say("Done."); $timeoutPause->end(); }
public function testFilter() { $array = a(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); $array = $array->filter(function ($element) { return CMathi::isEven($element); }); $this->assertTrue($array->equals(a(2, 4, 6, 8, 10))); }
public function testFilter() { $array = CArray::fromElements(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); $array = CArray::filter($array, function ($element) { return CMathi::isEven($element); }); $this->assertTrue(CArray::equals($array, CArray::fromElements(2, 4, 6, 8, 10))); }
/** * Determines the order in which two maps should appear in a place where it matters. * * You can use your own comparator for the comparison of the values in the maps, but the default comparator has got * you covered when comparing scalar values, such as `string`, `int`, `float`, and `bool` in the ascending order or * in the descending order if you use `CComparator::ORDER_DESC`. And the default comparator is smart enough to know * how to compare objects of those classes that conform to the IEqualityAndOrder interface (static or not), * including CArray and CMap. See the [CComparator](CComparator.html) class for more on this. * * @param map $map The first map for comparison. * @param map $toMap The second map for comparison. * @param callable $comparator **OPTIONAL. Default is** `CComparator::ORDER_ASC`. The function or method to be * used for the comparison of any two values. If this parameter is provided, the comparator should take two * parameters, with the first parameter being a value from the first map and the second parameter being a value * from the second map, and return `-1` if the value from the first map would need to go before the value from the * second map if the two were being ordered in separate, `1` if the other way around, and `0` if the two values are * equal. * * @return int A negative value (typically `-1`) if the first map should go before the second map, a positive value * (typically `1`) if the other way around, and `0` if the two maps are equal. * * @link CComparator.html CComparator */ public static function compare($map, $toMap, $comparator = CComparator::ORDER_ASC) { assert('is_cmap($map) && is_cmap($toMap)', vs(isset($this), get_defined_vars())); $map = parray($map); $toMap = parray($toMap); // Compare the keys. $keys = array_keys($map); $toKeys = array_keys($toMap); if (count($keys) != count($toKeys)) { return CMathi::sign(count($keys) - count($toKeys)); } for ($i = 0; $i < count($keys); $i++) { if ($keys[$i] !== $toKeys[$i]) { return $keys[$i] < $toKeys[$i] ? -1 : 1; } } // Compare the values. The quantities should match at this point. $values = array_values($map); $toValues = array_values($toMap); for ($i = 0; $i < count($values); $i++) { $compRes = call_user_func($comparator, $values[$i], $toValues[$i]); if ($compRes != 0) { return $compRes; } } return 0; }
public function testFilter() { $map = m(["one" => 1, "two" => 2, "three" => 3, "four" => 4, "five" => 5, "six" => 6, "seven" => 7, "eight" => 8, "nine" => 9, "ten" => 10]); $map = $map->filter(function ($value) { return CMathi::isEven($value); }); $this->assertTrue($map->equals(m(["two" => 2, "four" => 4, "six" => 6, "eight" => 8, "ten" => 10]))); }
/** * Converts an integer quantity of mass from one unit into another and returns the result. * * @param int $quantity The quantity to be converted. * @param enum $fromUnit The source unit. * @param enum $toUnit The destination unit. * * @return int The converted quantity, after rounding to the nearest integer. */ public static function convertMassi($quantity, $fromUnit, $toUnit) { assert('is_int($quantity) && is_enum($fromUnit) && is_enum($toUnit)', vs(isset($this), get_defined_vars())); assert('$quantity >= 0', vs(isset($this), get_defined_vars())); if ($fromUnit == $toUnit) { return $quantity; } $floatQuantity = (double) $quantity; $milligramQty; switch ($fromUnit) { case self::MILLIGRAM: $milligramQty = $floatQuantity; break; case self::GRAM: $milligramQty = $floatQuantity * 1000; break; case self::KILOGRAM: $milligramQty = $floatQuantity * 1000000; break; case self::TON: $milligramQty = $floatQuantity * 1000000000; break; case self::OUNCE: $milligramQty = $floatQuantity * 28349.5231; break; case self::POUND: $milligramQty = $floatQuantity * 453592.37; break; case self::STONE: $milligramQty = $floatQuantity * 6350293.18; break; case self::SHORT_TON: $milligramQty = $floatQuantity * 907184740; break; case self::LONG_TON: $milligramQty = $floatQuantity * 1016046908.8; break; default: assert('false', vs(isset($this), get_defined_vars())); break; } $outputQty; switch ($toUnit) { case self::MILLIGRAM: $outputQty = CMathi::round($milligramQty); break; case self::GRAM: $outputQty = CMathi::round($milligramQty / 1000); break; case self::KILOGRAM: $outputQty = CMathi::round($milligramQty / 1000000); break; case self::TON: $outputQty = CMathi::round($milligramQty / 1000000000); break; case self::OUNCE: $outputQty = CMathi::round($milligramQty / 28349.5231); break; case self::POUND: $outputQty = CMathi::round($milligramQty / 453592.37); break; case self::STONE: $outputQty = CMathi::round($milligramQty / 6350293.18); break; case self::SHORT_TON: $outputQty = CMathi::round($milligramQty / 907184740); break; case self::LONG_TON: $outputQty = CMathi::round($milligramQty / 1016046908.8); break; default: assert('false', vs(isset($this), get_defined_vars())); break; } return $outputQty; }
/** * Returns the offset of a time zone from UTC, without adjusting it for daylight saving time. * * The offset is negative for the time zones located west of UTC (Greenwich, UK) and positive for the eastern ones. * * @return int The time zone's standard offset from UTC, in seconds. */ public function standardOffsetSeconds() { $itz = $this->ITimeZone(); $offset; $dstOffset; $itz->getOffset(time() * 1000, false, $offset, $dstOffset); return CMathi::round((double) $offset / 1000); }
public function testRoundToPow2Up() { $this->assertTrue(CMathi::roundToPow2Up(3) === 4); $this->assertTrue(CMathi::roundToPow2Up(5) === 8); $this->assertTrue(CMathi::roundToPow2Up(8) === 8); $this->assertTrue(CMathi::roundToPow2Up(600) === 1024); $this->assertTrue(CMathi::roundToPow2Up(3.01) === 4); $this->assertTrue(CMathi::roundToPow2Up(5.01) === 8); $this->assertTrue(CMathi::roundToPow2Up(8.01) === 8); $this->assertTrue(CMathi::roundToPow2Up(600.01) === 1024); }