diff --git a/src/Bc.php b/src/Bc.php index 9e4ca33..df9d77e 100644 --- a/src/Bc.php +++ b/src/Bc.php @@ -1,389 +1 @@ - self::add($carry, (string) $item, $scale), - '0', - ); - } - - /** - * Subtract $b from $a. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - * @return numeric-string - */ - public static function sub(string $a, string $b, int $scale = 8): string - { - return bcsub( - Guard::isNumeric($a), - Guard::isNumeric($b), - $scale, - ); - } - - /** - * Multiply two arbitrary-precision decimals. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - * @return numeric-string - */ - public static function mul(string $a, string $b, int $scale = 8): string - { - return bcmul( - Guard::isNumeric($a), - Guard::isNumeric($b), - $scale, - ); - } - - /** - * Divide $a by $b. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - * @throws \DivisionByZeroError if $b is zero - * @return numeric-string - */ - public static function div(string $a, string $b, int $scale = 8): string - { - Guard::that( - !self::isZero($b, $scale + 1), - static fn() => new \DivisionByZeroError('Division by zero.'), - ); - - return bcdiv(Guard::isNumeric($a), $b, $scale); - } - - /** - * Normalize a numeric string to exactly $scale decimal places (truncates, does not round). - * - * Example: Bc::scale('1.5', 8) → '1.50000000' - * - * @param numeric-string $value - * @throws \Throwable - * @return numeric-string - */ - public static function scale(string $value, int $scale = 8): string - { - return self::add( - $value, - '0', - $scale, - ); - } - - /** - * Return the absolute value. - * - * @param numeric-string $value - * @throws \Throwable - * @return numeric-string - */ - public static function abs(string $value, int $scale = 8): string - { - return self::isNegative($value, $scale) - ? self::sub('0', $value, $scale) - : self::add($value, '0', $scale); - } - - /** - * Return the larger of two values. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - * @return numeric-string - */ - public static function max(string $a, string $b, int $scale = 8): string - { - return self::gte($a, $b, $scale) - ? self::add($a, '0', $scale) - : self::add($b, '0', $scale); - } - - /** - * Return the smaller of two values. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - * @return numeric-string - */ - public static function min(string $a, string $b, int $scale = 8): string - { - return self::lte($a, $b, $scale) - ? self::add($a, '0', $scale) - : self::add($b, '0', $scale); - } - - /** - * Compare two values. Returns -1, 0, or 1. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - */ - public static function compare(string $a, string $b, int $scale = 8): int - { - return bccomp( - Guard::isNumeric($a), - Guard::isNumeric($b), - $scale, - ); - } - - /** - * Return true when the value is exactly zero at the given scale. - * - * @param numeric-string $value - * @throws \Throwable - */ - public static function isZero(string $value, int $scale = 8): bool - { - return 0 === self::compare($value, '0', $scale); - } - - /** - * Return true when $a is greater than $b. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - */ - public static function gt(string $a, string $b, int $scale = 8): bool - { - return 1 === self::compare($a, $b, $scale); - } - - /** - * Return true when $a is less than $b. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - */ - public static function lt(string $a, string $b, int $scale = 8): bool - { - return -1 === self::compare($a, $b, $scale); - } - - /** - * Return true when $a is greater than or equal to $b. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - */ - public static function gte(string $a, string $b, int $scale = 8): bool - { - return self::compare($a, $b, $scale) >= 0; - } - - /** - * Return true when $a is less than or equal to $b. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - */ - public static function lte(string $a, string $b, int $scale = 8): bool - { - return self::compare($a, $b, $scale) <= 0; - } - - /** - * Return true when $a equals $b at the given scale. - * - * @param numeric-string $a - * @param numeric-string $b - * @throws \Throwable - */ - public static function eq(string $a, string $b, int $scale = 8): bool - { - return 0 === self::compare($a, $b, $scale); - } - - /** - * Return true when $value is strictly greater than zero. - * - * @param numeric-string $value - * @throws \Throwable - */ - public static function isPositive(string $value, int $scale = 8): bool - { - return 1 === self::compare($value, '0', $scale); - } - - /** - * Return true when $value is strictly less than zero. - * - * @param numeric-string $value - * @throws \Throwable - */ - public static function isNegative(string $value, int $scale = 8): bool - { - return -1 === self::compare($value, '0', $scale); - } - - /** - * Negate a value (equivalent to multiplying by -1). - * - * @param numeric-string $value - * @throws \Throwable - * @return numeric-string - */ - public static function negate(string $value, int $scale = 8): string - { - return self::sub( - '0', - $value, - $scale, - ); - } - - /** - * Calculate $percent % of $value (i.e. value × percent ÷ 100). - * - * Example: Bc::percent('200', '15') → '30.00000000' - * - * @param numeric-string $value - * @param numeric-string $percent - * @throws \Throwable - * @return numeric-string - */ - public static function percent(string $value, string $percent, int $scale = 8): string - { - return self::div( - self::mul( - $value, - $percent, - $scale + 2, - ), - '100', - $scale, - ); - } - - /** - * Multiply an array of arbitrary-precision decimals together. - * - * Returns '1.00000000' for an empty array (neutral element of multiplication). - * - * @param numeric-string[] $operands - * @throws \Throwable - * @return numeric-string - */ - public static function product(array $operands, int $scale = 8): string - { - if ($operands === []) { - return self::add('1', '0', $scale); - } - - return array_reduce( - $operands, - static fn(string $carry, string $item) => self::mul($carry, (string) $item, $scale), - '1', - ); - } - - /** - * Subtract all subsequent operands from the first. - * - * Returns '0.00000000' for an empty array. - * - * @param numeric-string[] $operands - * @throws \Throwable - * @return numeric-string - */ - public static function subAll(array $operands, int $scale = 8): string - { - if ($operands === []) { - return self::add('0', '0', $scale); - } - - $first = (string) array_shift($operands); - - return array_reduce( - $operands, - static fn(string $carry, string $item) => self::sub($carry, (string) $item, $scale), - $first, - ); - } - - /** - * Divide the first operand by each subsequent operand in turn. - * - * Returns '1.00000000' for an empty array (neutral element of division). - * - * @param numeric-string[] $operands - * @throws \Throwable - * @return numeric-string - */ - public static function divAll(array $operands, int $scale = 8): string - { - if ($operands === []) { - return self::add('1', '0', $scale); - } - - $first = (string) array_shift($operands); - - return array_reduce( - $operands, - static fn(string $carry, string $item) => self::div($carry, (string) $item, $scale), - $first, - ); - } -} +PD9waHAKCmRlY2xhcmUoc3RyaWN0X3R5cGVzPTEpOwoKbmFtZXNwYWNlIFRlelxVdGlsc1xOdW07Cgp1c2UgVGV6XFV0aWxzXFZhbGlkYXRlXEd1YXJkOwoKLyoqCiAqIEFyYml0cmFyeS1wcmVjaXNpb24gZGVjaW1hbCBhcml0aG1ldGljIGhlbHBlcnMgd3JhcHBpbmcgUEhQJ3MgYmNtYXRoIGV4dGVuc2lvbi4KICoKICogQWxsIG1ldGhvZHMgZGVmYXVsdCB0byA4IGRlY2ltYWwgcGxhY2VzLCB3aGljaCBpcyBhcHByb3ByaWF0ZSBmb3IgZmluYW5jaWFsCiAqIGNhbGN1bGF0aW9ucy4gSW5wdXRzIG1heSBiZSBpbnQsIGZsb2F0LCBvciBudW1lcmljIHN0cmluZy4KICogUGFzc2luZyBhIG5vbi1udW1lcmljIHN0cmluZyB0byBhbnkgbWV0aG9kIHRocm93cyBcSW52YWxpZEFyZ3VtZW50RXhjZXB0aW9uLgogKi8KZmluYWwgY2xhc3MgQmMKewogICAgLyoqCiAgICAgKiBBZGQgdHdvIGFyYml0cmFyeS1wcmVjaXNpb24gZGVjaW1hbHMuCiAgICAgKgogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJGEKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICRiCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqIEByZXR1cm4gbnVtZXJpYy1zdHJpbmcKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBhZGQoaW50fGZsb2F0fHN0cmluZyAkYSwgaW50fGZsb2F0fHN0cmluZyAkYiwgaW50ICRzY2FsZSA9IDgpOiBzdHJpbmcKICAgIHsKICAgICAgICByZXR1cm4gYmNhZGQoCiAgICAgICAgICAgIEd1YXJkOjppc051bWVyaWMoKHN0cmluZykgJGEpLAogICAgICAgICAgICBHdWFyZDo6aXNOdW1lcmljKChzdHJpbmcpICRiKSwKICAgICAgICAgICAgJHNjYWxlLAogICAgICAgICk7CiAgICB9CgogICAgLyoqCiAgICAgKiBTdW0gYW4gYXJyYXkgb2YgYXJiaXRyYXJ5LXByZWNpc2lvbiBkZWNpbWFscy4KICAgICAqCiAgICAgKiBSZXR1cm5zIGEgY29ycmVjdGx5IHNjYWxlZCB6ZXJvICgnMC4wMDAwMDAwMCcpIGZvciBhbiBlbXB0eSBhcnJheS4KICAgICAqCiAgICAgKiBAcGFyYW0gYXJyYXk8aW50fGZsb2F0fHN0cmluZz4gJG9wZXJhbmRzCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqIEByZXR1cm4gbnVtZXJpYy1zdHJpbmcKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBzdW0oYXJyYXkgJG9wZXJhbmRzLCBpbnQgJHNjYWxlID0gOCk6IHN0cmluZwogICAgewogICAgICAgIGlmICgkb3BlcmFuZHMgPT09IFtdKSB7CiAgICAgICAgICAgIHJldHVybiBzZWxmOjphZGQoJzAnLCAnMCcsICRzY2FsZSk7CiAgICAgICAgfQoKICAgICAgICByZXR1cm4gYXJyYXlfcmVkdWNlKAogICAgICAgICAgICAkb3BlcmFuZHMsCiAgICAgICAgICAgIHN0YXRpYyBmbihzdHJpbmcgJGNhcnJ5LCBpbnR8ZmxvYXR8c3RyaW5nICRpdGVtKSA9PiBzZWxmOjphZGQoJGNhcnJ5LCAoc3RyaW5nKSAkaXRlbSwgJHNjYWxlKSwKICAgICAgICAgICAgJzAnLAogICAgICAgICk7CiAgICB9CgogICAgLyoqCiAgICAgKiBTdWJ0cmFjdCAkYiBmcm9tICRhLgogICAgICoKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICRhCiAgICAgKiBAcGFyYW0gaW50fGZsb2F0fHN0cmluZyAkYgogICAgICogQHRocm93cyBcVGhyb3dhYmxlCiAgICAgKiBAcmV0dXJuIG51bWVyaWMtc3RyaW5nCiAgICAgKi8KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gc3ViKGludHxmbG9hdHxzdHJpbmcgJGEsIGludHxmbG9hdHxzdHJpbmcgJGIsIGludCAkc2NhbGUgPSA4KTogc3RyaW5nCiAgICB7CiAgICAgICAgcmV0dXJuIGJjc3ViKAogICAgICAgICAgICBHdWFyZDo6aXNOdW1lcmljKChzdHJpbmcpICRhKSwKICAgICAgICAgICAgR3VhcmQ6OmlzTnVtZXJpYygoc3RyaW5nKSAkYiksCiAgICAgICAgICAgICRzY2FsZSwKICAgICAgICApOwogICAgfQoKICAgIC8qKgogICAgICogTXVsdGlwbHkgdHdvIGFyYml0cmFyeS1wcmVjaXNpb24gZGVjaW1hbHMuCiAgICAgKgogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJGEKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICRiCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqIEByZXR1cm4gbnVtZXJpYy1zdHJpbmcKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBtdWwoaW50fGZsb2F0fHN0cmluZyAkYSwgaW50fGZsb2F0fHN0cmluZyAkYiwgaW50ICRzY2FsZSA9IDgpOiBzdHJpbmcKICAgIHsKICAgICAgICByZXR1cm4gYmNtdWwoCiAgICAgICAgICAgIEd1YXJkOjppc051bWVyaWMoKHN0cmluZykgJGEpLAogICAgICAgICAgICBHdWFyZDo6aXNOdW1lcmljKChzdHJpbmcpICRiKSwKICAgICAgICAgICAgJHNjYWxlLAogICAgICAgICk7CiAgICB9CgogICAgLyoqCiAgICAgKiBEaXZpZGUgJGEgYnkgJGIuCiAgICAgKgogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJGEKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICRiCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqIEB0aHJvd3MgXERpdmlzaW9uQnlaZXJvRXJyb3IgaWYgJGIgaXMgemVybwogICAgICogQHJldHVybiBudW1lcmljLXN0cmluZwogICAgICovCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGRpdihpbnR8ZmxvYXR8c3RyaW5nICRhLCBpbnR8ZmxvYXR8c3RyaW5nICRiLCBpbnQgJHNjYWxlID0gOCk6IHN0cmluZwogICAgewogICAgICAgIEd1YXJkOjp0aGF0KAogICAgICAgICAgICAhc2VsZjo6aXNaZXJvKCRiLCAkc2NhbGUgKyAxKSwKICAgICAgICAgICAgc3RhdGljIGZuKCkgPT4gbmV3IFxEaXZpc2lvbkJ5WmVyb0Vycm9yKCdEaXZpc2lvbiBieSB6ZXJvLicpLAogICAgICAgICk7CgogICAgICAgIHJldHVybiBiY2RpdihHdWFyZDo6aXNOdW1lcmljKChzdHJpbmcpICRhKSwgKHN0cmluZykgJGIsICRzY2FsZSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBOb3JtYWxpemUgYSBudW1lcmljIHZhbHVlIHRvIGV4YWN0bHkgJHNjYWxlIGRlY2ltYWwgcGxhY2VzICh0cnVuY2F0ZXMsIGRvZXMgbm90IHJvdW5kKS4KICAgICAqCiAgICAgKiBFeGFtcGxlOiBCYzo6c2NhbGUoMS41LCA4KSDigpIgJzEuNTAwMDAwMDAnCiAgICAgKgogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJHZhbHVlCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqIEByZXR1cm4gbnVtZXJpYy1zdHJpbmcKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBzY2FsZShpbnR8ZmxvYXR8c3RyaW5nICR2YWx1ZSwgaW50ICRzY2FsZSA9IDgpOiBzdHJpbmcKICAgIHsKICAgICAgICByZXR1cm4gc2VsZjo6YWRkKAogICAgICAgICAgICAkdmFsdWUsCiAgICAgICAgICAgICcwJywKICAgICAgICAgICAgJHNjYWxlLAogICAgICAgICk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gdGhlIGFic29sdXRlIHZhbHVlLgogICAgICoKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICR2YWx1ZQogICAgICogQHRocm93cyBcVGhyb3dhYmxlCiAgICAgKiBAcmV0dXJuIG51bWVyaWMtc3RyaW5nCiAgICAgKi8KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gYWJzKGludHxmbG9hdHxzdHJpbmcgJHZhbHVlLCBpbnQgJHNjYWxlID0gOCk6IHN0cmluZwogICAgewogICAgICAgIHJldHVybiBzZWxmOjppc05lZ2F0aXZlKCR2YWx1ZSwgJHNjYWxlKQogICAgICAgICAgICA/IHNlbGY6OnN1YignMCcsICR2YWx1ZSwgJHNjYWxlKQogICAgICAgICAgICA6IHNlbGY6OmFkZCgkdmFsdWUsICcwJywgJHNjYWxlKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiB0aGUgbGFyZ2VyIG9mIHR3byB2YWx1ZXMuCiAgICAgKgogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJGEKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICRiCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqIEByZXR1cm4gbnVtZXJpYy1zdHJpbmcKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBtYXgoaW50fGZsb2F0fHN0cmluZyAkYSwgaW50fGZsb2F0fHN0cmluZyAkYiwgaW50ICRzY2FsZSA9IDgpOiBzdHJpbmcKICAgIHsKICAgICAgICByZXR1cm4gc2VsZjo6Z3RlKCRhLCAkYiwgJHNjYWxlKQogICAgICAgICAgICA/IHNlbGY6OmFkZCgkYSwgJzAnLCAkc2NhbGUpCiAgICAgICAgICAgIDogc2VsZjo6YWRkKCRiLCAnMCcsICRzY2FsZSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gdGhlIHNtYWxsZXIgb2YgdHdvIHZhbHVlcy4KICAgICAqCiAgICAgKiBAcGFyYW0gaW50fGZsb2F0fHN0cmluZyAkYQogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJGIKICAgICAqIEB0aHJvd3MgXFRocm93YWJsZQogICAgICogQHJldHVybiBudW1lcmljLXN0cmluZwogICAgICovCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIG1pbihpbnR8ZmxvYXR8c3RyaW5nICRhLCBpbnR8ZmxvYXR8c3RyaW5nICRiLCBpbnQgJHNjYWxlID0gOCk6IHN0cmluZwogICAgewogICAgICAgIHJldHVybiBzZWxmOjpsdGUoJGEsICRiLCAkc2NhbGUpCiAgICAgICAgICAgID8gc2VsZjo6YWRkKCRhLCAnMCcsICRzY2FsZSkKICAgICAgICAgICAgOiBzZWxmOjphZGQoJGIsICcwJywgJHNjYWxlKTsKICAgIH0KCiAgICAvKioKICAgICAqIENvbXBhcmUgdHdvIHZhbHVlcy4gUmV0dXJucyAtMSwgMCwgb3IgMS4KICAgICAqCiAgICAgKiBAcGFyYW0gaW50fGZsb2F0fHN0cmluZyAkYQogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJGIKICAgICAqIEB0aHJvd3MgXFRocm93YWJsZQogICAgICovCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGNvbXBhcmUoaW50fGZsb2F0fHN0cmluZyAkYSwgaW50fGZsb2F0fHN0cmluZyAkYiwgaW50ICRzY2FsZSA9IDgpOiBpbnQKICAgIHsKICAgICAgICByZXR1cm4gYmNjb21wKAogICAgICAgICAgICBHdWFyZDo6aXNOdW1lcmljKChzdHJpbmcpICRhKSwKICAgICAgICAgICAgR3VhcmQ6OmlzTnVtZXJpYygoc3RyaW5nKSAkYiksCiAgICAgICAgICAgICRzY2FsZSwKICAgICAgICApOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHRydWUgd2hlbiB0aGUgdmFsdWUgaXMgZXhhY3RseSB6ZXJvIGF0IHRoZSBnaXZlbiBzY2FsZS4KICAgICAqCiAgICAgKiBAcGFyYW0gaW50fGZsb2F0fHN0cmluZyAkdmFsdWUKICAgICAqIEB0aHJvd3MgXFRocm93YWJsZQogICAgICovCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGlzWmVybyhpbnR8ZmxvYXR8c3RyaW5nICR2YWx1ZSwgaW50ICRzY2FsZSA9IDgpOiBib29sCiAgICB7CiAgICAgICAgcmV0dXJuIDAgPT09IHNlbGY6OmNvbXBhcmUoJHZhbHVlLCAnMCcsICRzY2FsZSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gdHJ1ZSB3aGVuICRhIGlzIGdyZWF0ZXIgdGhhbiAkYi4KICAgICAqCiAgICAgKiBAcGFyYW0gaW50fGZsb2F0fHN0cmluZyAkYQogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJGIKICAgICAqIEB0aHJvd3MgXFRocm93YWJsZQogICAgICovCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGd0KGludHxmbG9hdHxzdHJpbmcgJGEsIGludHxmbG9hdHxzdHJpbmcgJGIsIGludCAkc2NhbGUgPSA4KTogYm9vbAogICAgewogICAgICAgIHJldHVybiAxID09PSBzZWxmOjpjb21wYXJlKCRhLCAkYiwgJHNjYWxlKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiB0cnVlIHdoZW4gJGEgaXMgbGVzcyB0aGFuICRiLgogICAgICoKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICRhCiAgICAgKiBAcGFyYW0gaW50fGZsb2F0fHN0cmluZyAkYgogICAgICogQHRocm93cyBcVGhyb3dhYmxlCiAgICAgKi8KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gbHQoaW50fGZsb2F0fHN0cmluZyAkYSwgaW50fGZsb2F0fHN0cmluZyAkYiwgaW50ICRzY2FsZSA9IDgpOiBib29sCiAgICB7CiAgICAgICAgcmV0dXJuIC0xID09PSBzZWxmOjpjb21wYXJlKCRhLCAkYiwgJHNjYWxlKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiB0cnVlIHdoZW4gJGEgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvICRiLgogICAgICoKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICRhCiAgICAgKiBAcGFyYW0gaW50fGZsb2F0fHN0cmluZyAkYgogICAgICogQHRocm93cyBcVGhyb3dhYmxlCiAgICAgKi8KICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gZ3RlKGludHxmbG9hdHxzdHJpbmcgJGEsIGludHxmbG9hdHxzdHJpbmcgJGIsIGludCAkc2NhbGUgPSA4KTogYm9vbAogICAgewogICAgICAgIHJldHVybiBzZWxmOjpjb21wYXJlKCRhLCAkYiwgJHNjYWxlKSA+PSAwOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHRydWUgd2hlbiAkYSBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gJGIuCiAgICAgKgogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJGEKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICRiCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBsdGUoaW50fGZsb2F0fHN0cmluZyAkYSwgaW50fGZsb2F0fHN0cmluZyAkYiwgaW50ICRzY2FsZSA9IDgpOiBib29sCiAgICB7CiAgICAgICAgcmV0dXJuIHNlbGY6OmNvbXBhcmUoJGEsICRiLCAkc2NhbGUpIDw9IDA7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gdHJ1ZSB3aGVuICRhIGVxdWFscyAkYiBhdCB0aGUgZ2l2ZW4gc2NhbGUuCiAgICAgKgogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJGEKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICRiCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBlcShpbnR8ZmxvYXR8c3RyaW5nICRhLCBpbnR8ZmxvYXR8c3RyaW5nICRiLCBpbnQgJHNjYWxlID0gOCk6IGJvb2wKICAgIHsKICAgICAgICByZXR1cm4gMCA9PT0gc2VsZjo6Y29tcGFyZSgkYSwgJGIsICRzY2FsZSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gdHJ1ZSB3aGVuICR2YWx1ZSBpcyBzdHJpY3RseSBncmVhdGVyIHRoYW4gemVyby4KICAgICAqCiAgICAgKiBAcGFyYW0gaW50fGZsb2F0fHN0cmluZyAkdmFsdWUKICAgICAqIEB0aHJvd3MgXFRocm93YWJsZQogICAgICovCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIGlzUG9zaXRpdmUoaW50fGZsb2F0fHN0cmluZyAkdmFsdWUsIGludCAkc2NhbGUgPSA4KTogYm9vbAogICAgewogICAgICAgIHJldHVybiAxID09PSBzZWxmOjpjb21wYXJlKCR2YWx1ZSwgJzAnLCAkc2NhbGUpOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHRydWUgd2hlbiAkdmFsdWUgaXMgc3RyaWN0bHkgbGVzcyB0aGFuIHplcm8uCiAgICAgKgogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJHZhbHVlCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBpc05lZ2F0aXZlKGludHxmbG9hdHxzdHJpbmcgJHZhbHVlLCBpbnQgJHNjYWxlID0gOCk6IGJvb2wKICAgIHsKICAgICAgICByZXR1cm4gLTEgPT09IHNlbGY6OmNvbXBhcmUoJHZhbHVlLCAnMCcsICRzY2FsZSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBOZWdhdGUgYSB2YWx1ZSAoZXF1aXZhbGVudCB0byBtdWx0aXBseWluZyBieSAtMSkuCiAgICAgKgogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJHZhbHVlCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqIEByZXR1cm4gbnVtZXJpYy1zdHJpbmcKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBuZWdhdGUoaW50fGZsb2F0fHN0cmluZyAkdmFsdWUsIGludCAkc2NhbGUgPSA4KTogc3RyaW5nCiAgICB7CiAgICAgICAgcmV0dXJuIHNlbGY6OnN1YigKICAgICAgICAgICAgJzAnLAogICAgICAgICAgICAkdmFsdWUsCiAgICAgICAgICAgICRzY2FsZSwKICAgICAgICApOwogICAgfQoKICAgIC8qKgogICAgICogQ2FsY3VsYXRlICRwZXJjZW50ICUgb2YgJHZhbHVlIChpLmUuIHZhbHVlIMOXIHBlcmNlbnQgw7cgMTAwKS4KICAgICAqCiAgICAgKiBFeGFtcGxlOiBCYzo6cGVyY2VudCgyMDAsIDE1KSDihpIgJzMwLjAwMDAwMDAwJwogICAgICoKICAgICAqIEBwYXJhbSBpbnR8ZmxvYXR8c3RyaW5nICR2YWx1ZQogICAgICogQHBhcmFtIGludHxmbG9hdHxzdHJpbmcgJHBlcmNlbnQKICAgICAqIEB0aHJvd3MgXFRocm93YWJsZQogICAgICogQHJldHVybiBudW1lcmljLXN0cmluZwogICAgICovCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIHBlcmNlbnQoaW50fGZsb2F0fHN0cmluZyAkdmFsdWUsIGludHxmbG9hdHxzdHJpbmcgJHBlcmNlbnQsIGludCAkc2NhbGUgPSA4KTogc3RyaW5nCiAgICB7CiAgICAgICAgcmV0dXJuIHNlbGY6OmRpdigKICAgICAgICAgICAgc2VsZjo6bXVsKAogICAgICAgICAgICAgICAgJHZhbHVlLAogICAgICAgICAgICAgICAgJHBlcmNlbnQsCiAgICAgICAgICAgICAgICAkc2NhbGUgKyAyLAogICAgICAgICAgICApLAogICAgICAgICAgICAnMTAwJywKICAgICAgICAgICAgJHNjYWxlLAogICAgICAgICk7CiAgICB9CgogICAgLyoqCiAgICAgKiBNdWx0aXBseSBhbiBhcnJheSBvZiBhcmJpdHJhcnktcHJlY2lzaW9uIGRlY2ltYWxzIHRvZ2V0aGVyLgogICAgICoKICAgICAqIFJldHVybnMgJzEuMDAwMDAwMDAnIGZvciBhbiBlbXB0eSBhcnJheSAobmV1dHJhbCBlbGVtZW50IG9mIG11bHRpcGxpY2F0aW9uKS4KICAgICAqCiAgICAgKiBAcGFyYW0gYXJyYXk8aW50fGZsb2F0fHN0cmluZz4gJG9wZXJhbmRzCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqIEByZXR1cm4gbnVtZXJpYy1zdHJpbmcKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBwcm9kdWN0KGFycmF5ICRvcGVyYW5kcywgaW50ICRzY2FsZSA9IDgpOiBzdHJpbmcKICAgIHsKICAgICAgICBpZiAoJG9wZXJhbmRzID09PSBbXSkgewogICAgICAgICAgICByZXR1cm4gc2VsZjo6YWRkKCcxJywgJzAnLCAkc2NhbGUpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIGFycmF5X3JlZHVjZSgKICAgICAgICAgICAgJG9wZXJhbmRzLAogICAgICAgICAgICBzdGF0aWMgZm4oc3RyaW5nICRjYXJyeSwgaW50fGZsb2F0fHN0cmluZyAkaXRlbSkgPT4gc2VsZjo6bXVsKCRjYXJyeSwgKHN0cmluZykgJGl0ZW0sICRzY2FsZSksCiAgICAgICAgICAgICcxJywKICAgICAgICApOwogICAgfQoKICAgIC8qKgogICAgICogU3VidHJhY3QgYWxsIHN1YnNlcXVlbnQgb3BlcmFuZHMgZnJvbSB0aGUgZmlyc3QuCiAgICAgKgogICAgICogUmV0dXJucyAnMC4wMDAwMDAwMCcgZm9yIGFuIGVtcHR5IGFycmF5LgogICAgICoKICAgICAqIEBwYXJhbSBhcnJheTxpbnR8ZmxvYXR8c3RyaW5nPiAkb3BlcmFuZHMKICAgICAqIEB0aHJvd3MgXFRocm93YWJsZQogICAgICogQHJldHVybiBudW1lcmljLXN0cmluZwogICAgICovCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIHN1YkFsbChhcnJheSAkb3BlcmFuZHMsIGludCAkc2NhbGUgPSA4KTogc3RyaW5nCiAgICB7CiAgICAgICAgaWYgKCRvcGVyYW5kcyA9PT0gW10pIHsKICAgICAgICAgICAgcmV0dXJuIHNlbGY6OmFkZCgnMCcsICcwJywgJHNjYWxlKTsKICAgICAgICB9CgogICAgICAgICRmaXJzdCA9IChzdHJpbmcpIGFycmF5X3NoaWZ0KCRvcGVyYW5kcyk7CgogICAgICAgIHJldHVybiBhcnJheV9yZWR1Y2UoCiAgICAgICAgICAgICRvcGVyYW5kcywKICAgICAgICAgICAgc3RhdGljIGZuKHN0cmluZyAkY2FycnksIGludHxmbG9hdHxzdHJpbmcgJGl0ZW0pID0+IHNlbGY6OnN1YigkY2FycnksIChzdHJpbmcpICRpdGVtLCAkc2NhbGUpLAogICAgICAgICAgICAkZmlyc3QsCiAgICAgICAgKTsKICAgIH0KCiAgICAvKioKICAgICAqIERpdmlkZSB0aGUgZmlyc3Qgb3BlcmFuZCBieSBlYWNoIHN1YnNlcXVlbnQgb3BlcmFuZCBpbiB0dXJuLgogICAgICoKICAgICAqIFJldHVybnMgJzEuMDAwMDAwMDAnIGZvciBhbiBlbXB0eSBhcnJheSAobmV1dHJhbCBlbGVtZW50IG9mIGRpdmlzaW9uKS4KICAgICAqCiAgICAgKiBAcGFyYW0gYXJyYXk8aW50fGZsb2F0fHN0cmluZz4gJG9wZXJhbmRzCiAgICAgKiBAdGhyb3dzIFxUaHJvd2FibGUKICAgICAqIEByZXR1cm4gbnVtZXJpYy1zdHJpbmcKICAgICAqLwogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBkaXZBbGwoYXJyYXkgJG9wZXJhbmRzLCBpbnQgJHNjYWxlID0gOCk6IHN0cmluZwogICAgewogICAgICAgIGlmICgkb3BlcmFuZHMgPT09IFtdKSB7CiAgICAgICAgICAgIHJldHVybiBzZWxmOjphZGQoJzEnLCAnMCcsICRzY2FsZSk7CiAgICAgICAgfQoKICAgICAgICAkZmlyc3QgPSAoc3RyaW5nKSBhcnJheV9zaGlmdCgkb3BlcmFuZHMpOwoKICAgICAgICByZXR1cm4gYXJyYXlfcmVkdWNlKAogICAgICAgICAgICAkb3BlcmFuZHMsCiAgICAgICAgICAgIHN0YXRpYyBmbihzdHJpbmcgJGNhcnJ5LCBpbnR8ZmxvYXR8c3RyaW5nICRpdGVtKSA9PiBzZWxmOjpkaXYoJGNhcnJ5LCAoc3RyaW5nKSAkaXRlbSwgJHNjYWxlKSwKICAgICAgICAgICAgJGZpcnN0LAogICAgICAgICk7CiAgICB9Cn0K \ No newline at end of file