JEMBOT MAWOT Bypass Shell
<?php
/**
* This file is part of the PrestaShop\Decimal package
*
* @author PrestaShop SA <contact@prestashop.com>
* @license https://opensource.org/licenses/MIT MIT License
*/
namespace PrestaShop\Decimal\Operation;
use PrestaShop\Decimal\DecimalNumber;
/**
* Allows transforming a decimal number's precision
*/
class Rounding
{
public const ROUND_TRUNCATE = 'truncate';
public const ROUND_CEIL = 'ceil';
public const ROUND_FLOOR = 'floor';
public const ROUND_HALF_UP = 'up';
public const ROUND_HALF_DOWN = 'down';
public const ROUND_HALF_EVEN = 'even';
/**
* Rounds a decimal number to a specified precision
*
* @param DecimalNumber $number Number to round
* @param int $precision Maximum number of decimals
* @param string $roundingMode Rounding algorithm
*
* @return DecimalNumber
*/
public function compute(DecimalNumber $number, $precision, $roundingMode)
{
switch ($roundingMode) {
case self::ROUND_HALF_UP:
return $this->roundHalfUp($number, $precision);
break;
case self::ROUND_CEIL:
return $this->ceil($number, $precision);
break;
case self::ROUND_FLOOR:
return $this->floor($number, $precision);
break;
case self::ROUND_HALF_DOWN:
return $this->roundHalfDown($number, $precision);
break;
case self::ROUND_TRUNCATE:
return $this->truncate($number, $precision);
break;
case self::ROUND_HALF_EVEN:
return $this->roundHalfEven($number, $precision);
break;
}
throw new \InvalidArgumentException(sprintf('Invalid rounding mode: %s', print_r($roundingMode, true)));
}
/**
* Truncates a number to a target number of decimal digits.
*
* @param DecimalNumber $number Number to round
* @param int $precision Maximum number of decimals
*
* @return DecimalNumber
*/
public function truncate(DecimalNumber $number, $precision)
{
$precision = $this->sanitizePrecision($precision);
if ($number->getPrecision() <= $precision) {
return $number;
}
if (0 === $precision) {
return new DecimalNumber($number->getSign() . $number->getIntegerPart());
}
return new DecimalNumber(
$number->getSign()
. $number->getIntegerPart()
. '.'
. substr($number->getFractionalPart(), 0, $precision)
);
}
/**
* Rounds a number up if its precision is greater than the target one.
*
* Ceil always rounds towards positive infinity.
*
* Examples:
*
* ```
* $n = new Decimal\Number('123.456');
* $this->ceil($n, 0); // '124'
* $this->ceil($n, 1); // '123.5'
* $this->ceil($n, 2); // '123.46'
*
* $n = new Decimal\Number('-123.456');
* $this->ceil($n, 0); // '-122'
* $this->ceil($n, 1); // '-123.3'
* $this->ceil($n, 2); // '-123.44'
* ```
*
* @param DecimalNumber $number Number to round
* @param int $precision Maximum number of decimals
*
* @return DecimalNumber
*/
public function ceil(DecimalNumber $number, $precision)
{
$precision = $this->sanitizePrecision($precision);
if ($number->getPrecision() <= $precision) {
return $number;
}
if ($number->isNegative()) {
// ceil works exactly as truncate for negative numbers
return $this->truncate($number, $precision);
}
/*
* The principle for ceil is the following:
*
* let X = number to round
* P = number of decimal digits that we want
* D = digit from the fractional part at index P
*
* if D > 0, ceil(X, P) = truncate(X + 10^(-P), P)
* if D = 0, ceil(X, P) = truncate(X, P)
*/
if ($precision > 0) {
// we know that D > 0, because we have already checked that the number's precision
// is greater than the target precision
$numberToAdd = '0.' . str_pad('1', $precision, '0', STR_PAD_LEFT);
} else {
$numberToAdd = '1';
}
return $this
->truncate($number, $precision)
->plus(new DecimalNumber($numberToAdd));
}
/**
* Rounds a number down if its precision is greater than the target one.
*
* Floor always rounds towards negative infinity.
*
* Examples:
*
* ```
* $n = new Decimal\Number('123.456');
* $this->floor($n, 0); // '123'
* $this->floor($n, 1); // '123.4'
* $this->floor($n, 2); // '123.45'
*
* $n = new Decimal\Number('-123.456');
* $this->floor($n, 0); // '-124'
* $this->floor($n, 1); // '-123.5'
* $this->floor($n, 2); // '-123.46'
* ```
*
* @param DecimalNumber $number Number to round
* @param int $precision Maximum number of decimals
*
* @return DecimalNumber
*/
public function floor(DecimalNumber $number, $precision)
{
$precision = $this->sanitizePrecision($precision);
if ($number->getPrecision() <= $precision) {
return $number;
}
if ($number->isPositive()) {
// floor works exactly as truncate for positive numbers
return $this->truncate($number, $precision);
}
/*
* The principle for ceil is the following:
*
* let X = number to round
* P = number of decimal digits that we want
* D = digit from the fractional part at index P
*
* if D < 0, ceil(X, P) = truncate(X - 10^(-P), P)
* if D = 0, ceil(X, P) = truncate(X, P)
*/
if ($precision > 0) {
// we know that D > 0, because we have already checked that the number's precision
// is greater than the target precision
$numberToSubtract = '0.' . str_pad('1', $precision, '0', STR_PAD_LEFT);
} else {
$numberToSubtract = '1';
}
return $this
->truncate($number, $precision)
->minus(new DecimalNumber($numberToSubtract));
}
/**
* Rounds the number according to the digit D located at precision P.
* - It rounds away from zero if D >= 5
* - It rounds towards zero if D < 5
*
* Examples:
*
* ```
* $n = new Decimal\Number('123.456');
* $this->roundHalfUp($n, 0); // '123'
* $this->roundHalfUp($n, 1); // '123.5'
* $this->roundHalfUp($n, 2); // '123.46'
*
* $n = new Decimal\Number('-123.456');
* $this->roundHalfUp($n, 0); // '-123'
* $this->roundHalfUp($n, 1); // '-123.5'
* $this->roundHalfUp($n, 2); // '-123.46'
* ```
*
* @param DecimalNumber $number Number to round
* @param int $precision Maximum number of decimals
*
* @return DecimalNumber
*/
public function roundHalfUp(DecimalNumber $number, $precision)
{
return $this->roundHalf($number, $precision, 5);
}
/**
* Rounds the number according to the digit D located at precision P.
* - It rounds away from zero if D > 5
* - It rounds towards zero if D <= 5
*
* Examples:
*
* ```
* $n = new Decimal\Number('123.456');
* $this->roundHalfUp($n, 0); // '123'
* $this->roundHalfUp($n, 1); // '123.4'
* $this->roundHalfUp($n, 2); // '123.46'
*
* $n = new Decimal\Number('-123.456');
* $this->roundHalfUp($n, 0); // '-123'
* $this->roundHalfUp($n, 1); // '-123.4'
* $this->roundHalfUp($n, 2); // '-123.46'
* ```
*
* @param DecimalNumber $number Number to round
* @param int $precision Maximum number of decimals
*
* @return DecimalNumber
*/
public function roundHalfDown(DecimalNumber $number, $precision)
{
return $this->roundHalf($number, $precision, 6);
}
/**
* Rounds a number according to "banker's rounding".
*
* The number is rounded according to the digit D located at precision P.
* - Away from zero if D > 5
* - Towards zero if D < 5
* - if D = 5, then
* - If the last significant digit is even, the number is rounded away from zero
* - If the last significant digit is odd, the number is rounded towards zero.
*
* Examples:
*
* ```
* $n = new Decimal\Number('123.456');
* $this->roundHalfUp($n, 0); // '123'
* $this->roundHalfUp($n, 1); // '123.4'
* $this->roundHalfUp($n, 2); // '123.46'
*
* $n = new Decimal\Number('-123.456');
* $this->roundHalfUp($n, 0); // '-123'
* $this->roundHalfUp($n, 1); // '-123.4'
* $this->roundHalfUp($n, 2); // '-123.46'
*
* $n = new Decimal\Number('1.1525354556575859505');
* $this->roundHalfEven($n, 0); // '1'
* $this->roundHalfEven($n, 1); // '1.2'
* $this->roundHalfEven($n, 2); // '1.15'
* $this->roundHalfEven($n, 3); // '1.152'
* $this->roundHalfEven($n, 4); // '1.1525'
* $this->roundHalfEven($n, 5); // '1.15255'
* $this->roundHalfEven($n, 6); // '1.152535'
* $this->roundHalfEven($n, 7); // '1.1525354'
* $this->roundHalfEven($n, 8); // '1.15253546'
* $this->roundHalfEven($n, 9); // '1.152535456'
* $this->roundHalfEven($n, 10); // '1.1525354556'
* ```
*
* @param DecimalNumber $number Number to round
* @param int $precision Maximum number of decimals
*
* @return DecimalNumber
*/
public function roundHalfEven(DecimalNumber $number, $precision)
{
$precision = $this->sanitizePrecision($precision);
if ($number->getPrecision() <= $precision) {
return $number;
}
/**
* The principle for roundHalfEven is the following:
*
* let X = number to round
* P = number of decimal digits that we want
* D = digit from the fractional part at index P
* E = digit to the left of D
*
* if D != 5, roundHalfEven(X, P) = roundHalfUp(X, P)
* if D = 5 and E is even, roundHalfEven(X, P) = truncate(X, P)
* if D = 5 and E is odd and X is positive, roundHalfUp(X, P) = ceil(X, P)
* if D = 5 and E is odd and X is negative, roundHalfUp(X, P) = floor(X, P)
*/
$fractionalPart = $number->getFractionalPart();
$digit = (int) $fractionalPart[$precision];
if ($digit !== 5) {
return $this->roundHalfUp($number, $precision);
}
// retrieve the digit to the left of it
if ($precision === 0) {
$referenceDigit = (int) substr($number->getIntegerPart(), -1);
} else {
$referenceDigit = (int) $fractionalPart[$precision - 1];
}
// truncate if even
$isEven = $referenceDigit % 2 === 0;
if ($isEven) {
return $this->truncate($number, $precision);
}
// round away from zero
$method = ($number->isPositive()) ? self::ROUND_CEIL : self::ROUND_FLOOR;
return $this->compute($number, $precision, $method);
}
/**
* Rounds the number according to the digit D located at precision P.
* - It rounds away from zero if D >= $halfwayValue
* - It rounds towards zero if D < $halfWayValue
*
* @param DecimalNumber $number Number to round
* @param int $precision Maximum number of decimals
* @param int $halfwayValue threshold upon which the rounding will be performed
* away from zero instead of towards zero
*
* @return DecimalNumber
*/
private function roundHalf(DecimalNumber $number, $precision, $halfwayValue)
{
$precision = $this->sanitizePrecision($precision);
if ($number->getPrecision() <= $precision) {
return $number;
}
/**
* The principle for roundHalf is the following:
*
* let X = number to round
* P = number of decimal digits that we want
* D = digit from the fractional part at index P
* Y = digit considered as the half-way value on which we round up (usually 5 or 6)
*
* if D >= Y, roundHalf(X, P) = ceil(X, P)
* if D < Y, roundHalf(X, P) = truncate(X, P)
*/
$fractionalPart = $number->getFractionalPart();
$digit = (int) $fractionalPart[$precision];
if ($digit >= $halfwayValue) {
// round away from zero
$mode = ($number->isPositive()) ? self::ROUND_CEIL : self::ROUND_FLOOR;
return $this->compute($number, $precision, $mode);
}
// round towards zero
return $this->truncate($number, $precision);
}
/**
* Ensures that precision is a positive int
*
* @param mixed $precision
*
* @return int Precision
*
* @throws \InvalidArgumentException if precision is not a positive integer
*/
private function sanitizePrecision($precision)
{
if (!is_numeric($precision) || $precision < 0) {
throw new \InvalidArgumentException(sprintf('Invalid precision: %s', print_r($precision, true)));
}
return (int) $precision;
}
}
xxxxx1.0, XXX xxxx